Как стать автором
Обновить

Доводы против самозакрывающихся тегов в HTML

Уровень сложностиПростой
Время на прочтение8 мин
Количество просмотров3.8K
Автор оригинала: Jake Archibald

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

<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 опираясь на статью Джейка Арчибальда. Советую ознакомиться.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Как вы относитесь к самозакрывающимся тегам?
36.04% Положительно. Меньше агли синтаксиса. Выучить самозакрывающиеся теги не сложно40
41.44% Нейтрально. Ну есть и есть. Жить не мешают46
22.52% Отрицательно. Они вводят путаницу. Не могу нормально спать по ночам25
Проголосовали 111 пользователей. Воздержались 16 пользователей.
Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
+7
Комментарии18

Публикации

Истории

Ближайшие события

One day offer от ВСК
Дата16 – 17 мая
Время09:00 – 18:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн
Антиконференция X5 Future Night
Дата30 мая
Время11:00 – 23:00
Место
Онлайн
Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург
Summer Merge
Дата28 – 30 июня
Время11:00
Место
Ульяновская область