Давайте поговорим о />:

<input type="text" />
<br />
<img src="…" />

Вы видели этот синтаксис в моем блоге, потому что это то, что делает Prettier и мне нравится Prettier, однако я не думаю, что вот это /> является чем-то хорошим.

Для начала:

Факты

Входим в XHTML

В конце 90-х и начале 2000-х годов W3C возлагал большие надежды на XML и считал, что он должен заменить HTML-синтаксис.

К этому были хорошие предпосылки. В то время не было спецификации для HTML-парсера и если задача была не тривиальной, то вы сталкивались с 4-мя браузерными движками, которые интерпретировали один и тот же HTML-документ 4-мя разными способами. С другой стороны, у XML был полностью описанный парсер.

Но перевести все сразу на XML было бы слишком большим изменением, поэтому в 2000-ые XHTML 1.0 стал некой рекомендацией и предлагал писать HTML в режиме совместимости с уже существующими HTML- и XML-парсерами.

Это означает, что:

<!-- Вместо этого: -->
<HTML LANG="en">

<!-- Вы бы написали так: -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

Мы же хотим отпугнуть новичков, верно?

<!-- Вместо этого: -->
<option value=foo selected>…</option>

<!-- Вы бы написали так: -->
<option value="foo" selected="selected">…</option>

А все потому, что в XML атрибуты требуют наличия значений, которые, в свою очередь, должны быть ограничены двойными кавычками.

И также:

<!-- Вместо этого: -->
<img src="…">

<!-- Вы бы написали так: -->
<img src="…" />

Потому что в XML теги должны быть явно закрытыми, а на случай самозакрывающихся тегов у XML есть краткая запись: />.

В XML такая запись <this/> была бы отформатированна без пробела до /, но из-за того, что Netscape Navigator 4 не мог справиться задачей, когда / шел прямо после атрибута, в том же <input type="text"/>, то спецификация порекомендовала добавлять пробел до /.

Но браузеры все это не заботило

Эти правила были для XML-парсеров, а так как документы обслуживались, как HTML(если вы один/одна из тех, кто обслуживал сайт как application/xhtml+xml, то вам не надо что-либо мне говорить), то эти синтаксические "дополнения под звездочкой" были просто проигнорированы.

В подобной записи <option selected="selected"> значение атрибута игнорировалось, так что вариант <option selected=""> также прекрасно работал, ровно как и <option selected="false">(значение false опускалось), но для консистентности решили, что повторять имя атрибута это хорошая идея.

Если вы забыли поставить двойные кавычки для значения атрибута, то браузер не жаловался, а продолжал свою работу по отображению страницы.

Если вы закрыли тег через />, то браузер видел это как ошибку парсинга и просто игнорировал. И вот на этом этапе я начал сталкиваться с проблемами.

<br /> br закрыт. Этот текст находится НЕ внутри br.

Но также:

<br> br закрыт. Этот текст находится НЕ внутри br.

И вот тут-то и возникает путаница:

<div /> div открыт. Этот текст находится внутри div

В XML <div /> был бы замозакрывающимся тегом, но не в HTML. В HTML не /> закрывает br, а br закрывает сам себя. Он является частью особого списка элементов, которые никогда не могу иметь детей и именно поэтому эти элементы являются замозакрывающимися. А вот <div /> не замозакрывающийся, так как div в этот список не входит.

Выходим из XHTML

"Переходная" фаза XHTML закончилась с приходом XHTML 1.1. В этом моменте спецификация требовала, что бы документ был обработан и распаршен, как XML. Правила парсинга XML были хорошо определены, за исключением, когда встречался неправильный синтаксис. Лучшее, что могли сделать браузеры, это показывать страницу с ошибкой, в противном случае каждый бы начал выдумывать что-то свое. И в ответ на это браузеры сказали... "нет, спасибо".

Ну, на самом деле они не прямо сказали "нет", они поддержали этот вариант и поддерживают его до сих пор. Вот правильный XHTML-документ, обрабатывающийся как application/xhtml+xml , и неправильный XHTML-документ. Но браузеры не видели в этом будущее.

Спросите себя сами: если вы зашли на страницу вашего местного врача-хирурга для того что бы найти часы работы, какой браузер лучше, тот, который отображает часы работы хирургии или тот, который отображает сообщение об ошибке XML-парсинга?

Одна из замечательных особенностей браузеров состоит в том, что они толерантны к ошибкам и браузеры не были заинтересованы в том, что бы от этого отказываться.

XHTML в конце-концов был предан забвению, потому что пришло то, от чего браузеры были в восторге:

Входим в HTML5

HTML5 вступил в игру в 2008 году и одним из значимых изменений, которое он принес, стала спецификация парсинга. И, в отличии от спецификации парсинга XML, спецификация HTML5 подробно описала что браузеры должны делать, когда они сталкиваются со странной или неправильной разметкой.

Спецификация отказалась от всех требований XML, которые были введены в XHTML, и опиралась на слабости парсеров HTML, существовавших в то время. Она стала обрабатывать /> особым образом, но только для того, что бы специально игнорировать такой синтаксис.

Входим в SVG внутри HTML

В начале 2010-х годов возможность включения <svg> в HTML была прописана в спецификации и начала появляться в браузерах.

<div>
  <svg viewBox="0 0 100 100">
    <circle cx="50" cy="50" r="50" />
  </svg>
</div>

Хотя SVG является XML-форматом, когда он встраивается в HTML-документ, то парсится HTML-парсером. Однако для достижения максимальной совместимости со скопированным SVG-контентом, когда HTML-парсер находится внутри тега svg, он переключается на режим "стороннего контента", где /> уже имеет значение.

<div/> div открыт. Текст внутри div

Тогда как:

<svg>
	<g><text>Внутри группы</text></g>
	<g/><text>Снаружи группы</text>
</svg>

Другой сторонний контент, такой как MathML, ведет себя похожим образом.

И вот каким образом сейчас обстоят дела. /> в основном не имеет значения в HTML-документах, за исключением стороннего контента.

Мнения

Хотя мы, как индустрия, в целом отказались от большинства требований XHTML, это оформление самозакрывающегося тега, похоже, сохранилось, несмотря на то, что является пережитком спецификации, которая была отменена более 10 лет назад. Некоторые даже включают пробел до /, который был добавлен для совместимости с движком браузера еще в прошлом тысячелетии.

Я думаю, что такой синтаксис рудиментарный и не считаю, что инструменты, такие как Prettier, должны его продвигать. Чтобы доказать свою правоту, я отвечу на контраргументы, которые прозвучали в одной из тем Twitter'а.

"Самозакрывающиеся теги облегчают чтение кода и полезны для начинающих! Вам не нужно помнить какие теги являются самозакрывающимися."

Снаружи стороннего контента элементы, которые являются замозакрывающимися, всегда будут самозакрывающимися. Все остальные элементы - не будут.

<input />Этот текст снаружи input'а.

<input>Этот текст снаружи input'а.</input>

<div>Этот текст внутри div.</div>

<div />Этот текст внутри div.

Примеры /> выше ничего не делают. Единственный способ понять, что <input /> приемлемый вариант, а <div /> нет - выучить и запомнить какие элементы самозакрывающиеся. Да, не лучший вариант, но что поделать.

Но должен ли /> работать, чтобы быть полезным? Комментарии к коду не "работают". Как и />, комментарии являются указанием, они могут вводить в заблуждение, но это не является весомым аргументом, для того чтобы вообще их не оставлять. Проблема с /> состоит в том, что такой синтаксис не выглядит, как комментарий и, что еще хуже, не ведет себя, как комментарий из-за правил вокруг стороннего контента.

Я думаю, что это относительно плохо для новичков. Представьте, что вы никогда до этого не видели <img src="...">. В таком случае вы увидете, что в отличие от других элементов, у такого элемента нет закрывающего тега. Дебагеры и валидаторы не считают это ошибкой, предполагая, что в этом элементе есть нечто особенное, что вы должны знать - ему не нужен закрывающий тег, он является самозакрывающимся и это его характерная черта.

Теперь представьте, что вы никогда до этого не видели <img src="..." />. Вы посмотрите на этот новый синтаксис, который вы только что для себя открыли, и выясните, что он означает самозакрывающийся тег. В этот момент почему бы вам не предположить, что <iframe /> тоже является самозакрывающимся? А такая запись <img src="…"></img> является допустимой? С учетом всего этого, я расстроен, что ресурс MDN использует самозакрывающиеся теги в их документации для новичков.

Дополнение: Вот пример такой путаницы.

"Самозакрывающиеся теги совместимы с JSX"

JSX и HTML - разные форматы. Они не совместимы друг с другом. Делать вид, что это не так, неверно.

<div>
	<span>Hello</span>
	<span>world</span>
</div>

HTML выше рендерится как "Hello world".

const Component = () => {
	<div>
		<span>Hello</span>
		<span>world</span>
	</div>
};

JSX выше рендерится как "Helloworld". Форматы работаю по разному.

<main>
	<div />
	Hello
</main>

В этом верхнем примере HTML текст находится внутри div.

const Component = () => (
	<main>
		<div />
		Hello
	</main>
);

В этом верхнем примере JSX текст уже снаружи div. Это другая система!

<div classname="foo"></div>

HTML выше создает div с атрибутом classname.

const Component = () => <div classname="foo"></div>;

JSX выше создает div с атрибутом class. Окей, это больше синтаксис React, чем JSX, но это довольно распространенный подход к использованию JSX.

Я не думаю, что есть какие-то аргументы ЗА совместимость JSX и HTML. Несмотря на схожие визуальные признаки, это разные форматы, которые работают по-разному.

"Это значит, что я могу парсить HTML парсером XML"

Назовите меня пуристом, но если я захочу спарсить HTML-документ, то воспользуюсь HTML-парсером. Я бы не стал пытаться написать JSON так, чтобы его можно было разобрать парсером YAML, поэтому не вижу причин делать то же самое с HTML и XML.

Существует много замечательных библиотек для парсинга HTML, практически для всех языков. И так как парсер предопределен, то результаты будут схожими.

"Самозакрывающиеся теги ускоряют парсинг разметки"

Мне доводилось слышать подобное пару раз и я думаю, что это происходит от предположения, что чем больше вы предоставите информации, тем лучше процессор сможет ее оптимизировать или что-то подобное в этом духе. Но давайте проведем сравнение двух кусков кода:

Парсим <br>:

  1. <: О, это новый тег!

  2. br: Теперь я знаю какой элемент создать .

  3. >: Конец тега, br не попадает в список открытых элементов, так как он самозакрывающийся.

Парсим <br/>:

  1. <: О, это новый тег!

  2. br: Теперь я знаю какой элемент создать .

  3. /: Я просто это проигнорирую.

  4. >: Конец тега, br не попадает в список открытых элементов, так как он самозакрывающийся.

То есть технически <br/> медленнее парсить, так как он содержит дополнительный ненужный /. На самом деле, сколь значительно медленнее он парситься не будет, быстрее тоже.

"Самозакрывающиеся теги выглядят красиво"

Это, конечно, субъективно. Первое время, когда я работал с кодом, который поддерживал написание />, я думал, что /> выглядит ужасно, но я привык к этому. Я также привык это пропускать.

Если красота это цель, то можем использовать <input type="text" ?>!

Но если серьезно, я думаю, что эстетике следует отступить назад, учитывая, насколько запутанным является синтаксис.

Prettier'у следует быть более требовательным

Я уважаю подход Prettier "или наш, или никакой другой", но явно не в этом месте.

Prettier поменяет <br> на <br/>, но не сделает ничего с <div />. По факту, если вы дадите ему <div/>, то он отформатирует его как <div />.

Я думаю, что Prettier'у следует или отбросить /> в случаях когда для парсера это не несет никакой пользы, или исправить места, где /> явно отсутствует. К примеру:

<div />
Hello

...следовало бы отформатировать в такой вариант:

<div>Hello</div>

...таким же образом, как он это делает для незакрытых тегов.

Или может HTML разрешит закрытие тегов повсеместно?

Большая проблема в том, что внутри одного и того же HTML-документа /> когда-то игнорируется, а когда-то нет. Можем ли мы иметь опцию для переключения правил парсинга, что бы /> всегда был необходим? Например, что бы <div/> был самозакрывающимся. Я уже создал топик с этой проблемой, но что-то мне подсказывает, что это так никуда дальше не двинется, по причине несовместимости с текущими библиотеками, особенно с теми, которые тесно граничат с безопасностью.

Вот такие дела.

p.s. примечание от переводчика: на YouTube канале Тио Брауна(Theo Browne) вышло видео, в котором он разбирает проблему самозакрывающихся тегов в HTML опираясь на статью Джейка Арчибальда. Советую ознакомиться.

Комментарии (18)


  1. gro
    27.04.2024 11:06
    +2

    Если вы уж решили, что эта тема настолько важна и заслуживает статьи на публичном ресурсе, а также одним из доводов привели скорость парсинга, тогда уж хотя бы эту скорость протестируйте, пожалуйста.


  1. k12th
    27.04.2024 11:06

    Что меня больше всего удивляет, так это то что люди (и роботы) до сих пор ставят пробел перед />.


    1. Zenitchik
      27.04.2024 11:06

      Без пробела - подсветка синтаксиса тупит.


      1. k12th
        27.04.2024 11:06

        А где именно?


        1. Zenitchik
          27.04.2024 11:06

          Хм... Не тупит. Проверил во всех редакторах, которыми пользуюсь.

          Когда-то где-то тупила, а потом в привычку вошло. Надо отвыкать.


          1. pqbd
            27.04.2024 11:06
            +9

            А мне с пробелом просто приятнее глазам :)


  1. Zenitchik
    27.04.2024 11:06
    +1

    Короче, я так понял, что единственный разумный аргумент против - то что браузеры дружно на это забили.


    1. baldr
      27.04.2024 11:06
      +1

      Как человек, повидавший уже многое и написавший довольно большое количество кода сам, я позволю себе немного переформулировать: большинство браузеров на это, скорее всего, забили, но мы хз что будет делать очередной новый мобильный китайский браузер, так что, на всякий случай, лучше помнить об этом и закрывать эти теги. С пробелом.


      1. Zenitchik
        27.04.2024 11:06
        +1

        Зависит от DOCTYPE. Если мы заявляем HTML5 - то не надо. Если XHTML - то надо. С пробелом.


  1. Mingun
    27.04.2024 11:06
    +5

    Я бы не стал пытаться написать JSON так, чтобы его можно было разобрать парсером YAML

    Эм… Плохой пример, ведь YAML является надмножеством JSON, то есть, JSON-документ — это буквально валидный YAML-документ. YAML специально спроектировали таким образом ради совместимости с JSON.


    1. slonopotamus
      27.04.2024 11:06

      YAML является надмножеством JSON

      Нет


      1. baldr
        27.04.2024 11:06
        +1

        Вы приводите ссылку на какую-то библиотеку для Perl, автор которой признается что ему не всегда удаётся правильно вывести JSON чтобы он читался YAML-парсером.

        Надо отметить что это личная проблема этого автора и пользователей его библиотеки.

        Спецификация YAML прямо говорит что YAML v1.2 имела своей прямой целью сделать YAML строгим надмножеством JSON.


        1. slonopotamus
          27.04.2024 11:06

          Товарищ по моей ссылке приводит примеры конкретных конструкций, допустимых в JSON, но недопустимых в YAML. И о том что говорит спецификация YAML он тоже упоминает.


          1. baldr
            27.04.2024 11:06
            +1

            Да, он довольно категорично утверждает, но не приводит никакого конкретного списка несовместимостей. Честно говоря, я не уверен что даже каждый парсер самого JSON может нормально переварить мегабайтный ключ со странными Unicode-символами, или что он там ему скармливает.


        1. Zenitchik
          27.04.2024 11:06

          А как у YAML с отформатированным JSON? Не спотыкается? (Реально интересно, я сам не пробовал)


      1. Mingun
        27.04.2024 11:06

        Интересно. По ссылке автор утверждает, что даже спецификация YAML 1.2 (в которой самой прямым текстом сказано, что её цель была устранить все ранее выявленные несовместимости с JSON прошлых версий спецификации) по-прежнему остаётся несовместимой. Правда, на текущий момент последней является ревизия 1.2.2 от 2021-10-01, а текст по ссылке написан 2020-10-27, может быть, что-то и поменялось с тех пор. К сожалению, без более глубокого погружения в текст спецификации нельзя сказать, действительно ли это её проблема, либо же имеет место бага в приведённой библиотеке.


  1. PaulZi
    27.04.2024 11:06
    +3

    А как быть с web components и кастомными элементами? Браузер не будет знать, должен этот тэг быть самозакрывающимся всегда или нет, поэтому не определено поведение на случай не закрытого тэга. Поэтому рекомендация "давайте не будем использовать само закрывающийся синтаксис" означает что мы не можем писать <custom-input/>


  1. YuriyBakutin
    27.04.2024 11:06

    В шаблоне Vue самозакрывающийся div вполне можно использовать. Особенно с атрибутом v-html. Парсится правильно. Но, на мой взгляд, это плохая практика. Лучше всё-таки поставить закрывающий </div>, чтобы не плодить диалекты.