HTML5 — название, которое до сих пор висит в вакансиях, описаниях курсов и на обложках книжек. При этом стандарта с номером «5» уже несколько лет не существует: последняя нумерованная редакция закрылась, а спецификация с тех пор живёт в режиме непрерывного обновления. Само же слово прижилось и обычно означает «современный HTML» — тот, в котором есть семантические теги, встроенное видео, хранилище в браузере и набор браузерных API.
Понять, почему так вышло, проще всего через историю языка. Она короче, чем кажется, и почти вся состоит из решений, принятых на бегу: тег для картинок предложил не автор языка, мигающий текст появился после разговора в баре, а самая амбициозная версия стандарта умерла, не выйдя из черновика. Многое из того, что сегодня выглядит странностью HTML — необязательные закрывающие теги, терпимость к кривой разметке, короткий доктайп, — следствие именно этих поворотов.
Поэтому начнём с истории и десятка фактов о том, как язык разметки стал таким, какой он есть. Потом разберёмся, почему нумерация версий на пятёрке закончилась, что из «фишек HTML5» реально работает сегодня, какие советы из тех же подборок успели протухнуть и что HTML получил уже после того, как о нём перестали писать восторженные статьи.
Как HTML дошёл до пятёрки
Язык разметки родился как побочный продукт рабочей задачи. В марте 1989 года Тим Бернерс-Ли, тогда сотрудник CERN, подал руководству служебную записку про то, что в лаборатории невозможно найти документ: у каждой группы свои машины, свои форматы, своя картотека, а люди приходят и уходят вместе со знаниями. Предложенное решение — связать документы перекрёстными ссылками так, чтобы структура хранения перестала иметь значение.
Первое публичное описание языка появилось в конце 1991 года и называлось буднично — «HTML Tags». В нём было восемнадцать тегов: заголовки, абзац, списки, адрес и ссылка. Ни картинок, ни таблиц, ни форм, ни единого способа повлиять на оформление. Документ той эпохи выглядел примерно так:
<TITLE>Отчёт группы</TITLE>
<H1>Отчёт группы</H1>
<P>Черновик от 12 ноября. Замечания присылайте по почте.
<UL>
<LI><A HREF="methods.html">Методика</A>
<LI><A HREF="results.html">Результаты</A>
</UL>
<ADDRESS>Группа обработки данных</ADDRESS>
Закрывающих тегов у абзацев и пунктов списка нет не потому, что автор поленился: их и не требовалось. Эта особенность дожила до сегодняшнего дня — о ней ещё пойдёт речь ниже.
Дальше язык рос рывками, и росли его в первую очередь не комитеты, а производители браузеров: кто-то придумывал тег, реализовывал его у себя, остальные догоняли, а стандарт задним числом описывал то, что уже прижилось. Отсюда в HTML и картинки, и таблицы, и целый пласт элементов, от которых потом пришлось избавляться.
| Версия | Год | Что появилось |
| HTML (без номера) | 1991 | Первый публичный список тегов: заголовки, абзацы, списки, ссылки. Всего восемнадцать элементов. |
| HTML 2.0 | 1995 | Первая формальная спецификация. Зафиксировала то, что браузеры уже умели, и добавила формы. |
| HTML 3.2 | 1997 | Таблицы, апплеты, обтекание текста вокруг картинок, оформление прямо в разметке. |
| HTML 4.01 | 1999 | Разделение содержимого и оформления: таблицы стилей, скрипты, фреймы, первые атрибуты для доступности. |
| XHTML 1.0 | 2000 | Попытка переписать HTML по строгим правилам XML. Любая опечатка в разметке роняла страницу целиком. |
| HTML5 | 2014 | Семантические элементы, встроенные видео и аудио, холст для рисования, хранилище на клиенте, набор браузерных API. |
Между 2000-м и 2014-м зияет дыра в четырнадцать лет — и это самое интересное место в таблице. Комитет в те годы тянул веб в сторону XHTML 2.0, несовместимого со всем, что уже было написано: страница с одной опечаткой должна была показывать не содержимое, а сообщение об ошибке. Производителям браузеров такой план не понравился, и в 2004 году Apple, Mozilla и Opera собрали собственную рабочую группу — WHATWG — и начали писать свою спецификацию, отталкиваясь от того, как страницы верстают на практике. Через несколько лет комитет признал поражение, и наработки WHATWG вышли под именем HTML5.
Десять фактов из истории HTML
Сухая хронология выше прячет всё самое любопытное. Вот десяток деталей, которые объясняют характер языка лучше, чем список версий.
- «Расплывчато, но занятно». Такую резолюцию оставил на первом предложении Бернерса-Ли его руководитель Майк Сендалл. Проект всё-таки разрешили — правда, неофициально, как эксперимент в свободное время.
- Угловые скобки достались по наследству. Синтаксис взят из SGMLguid — внутреннего формата документации CERN, основанного на SGML. Из всего первого набора по-настоящему новым был только тег ссылки: гипертекст — та самая идея, ради которой всё затевалось.
- Тег картинки предложил не автор языка. В феврале 1993 года Марк Андриссен, работавший над браузером Mosaic, написал в рассылку www-talk, что хочет добавить <img>. Бернерс-Ли отвечал, что менять язык до выхода стандарта не стоит, обсуждались и другие имена — <icon>, <include>, — но Mosaic к тому моменту уже умел показывать картинки, и спор потерял смысл.
- Первые три года на страницах нельзя было ничего заполнить. Формы появились только во второй версии, в 1995 году. Она же единственная, которая вышла не документом W3C, а интернет-стандартом RFC 1866 — языком разметки тогда занимался IETF.
- Версии 3.0 не существует. Черновик получился слишком амбициозным: в нём были математические формулы, сложные таблицы и много всего, чего браузеры не потянули. Его выбросили и выпустили урезанную 3.2 — поэтому в таблице выше и стоит дробный номер.
- Мигающий текст придумали в баре. Лу Монтулли из Netscape заметил в разговоре с коллегами, что текстовый браузер Lynx не умеет почти ничего из предлагаемых красивостей — разве что мигание. Наутро кто-то уже реализовал тег. В релизе Netscape его даже не задокументировали, но про мигающий текст всё равно узнали все. Microsoft ответил тегом с бегущей строкой, и позже оба убрали одной сделкой: вы отказываетесь от своего, мы от своего.
- HTML5 задумывался не как HTML. В 2004 году WHATWG начал с двух черновиков — Web Applications 1.0 и Web Forms 2.0. Их слили в один, в 2007-м переименовали в HTML5, а ещё через несколько лет — просто в HTML, без номера.
- XHTML 2.0 закрыли официально. В 2009 году W3C объявил, что не продлевает устав рабочей группы, а освободившиеся силы отправляет на HTML5. Восемь лет работы закрыли одним пресс-релизом.
- HTML5 первым описал, что делать с ошибками. Раньше стандарт рассказывал только про правильную разметку, а как разбирать сломанную — каждый браузер решал сам, и одна и та же страница в двух браузерах могла выглядеть по-разному. Алгоритм разбора со всеми ветками «если автор написал ерунду» занял в спецификации больше места, чем описание самих тегов.
- Половину тегов можно не писать. Прямое следствие предыдущего пункта: спецификация честно перечисляет необязательные теги. Документ ниже валиден целиком — браузер сам достроит недостающее.
<!DOCTYPE html>
<title>Страница без единого лишнего тега</title>
<p>Тегов html, head и body здесь нет
<ul>
<li>закрывающих у пунктов списка тоже
<li>и у абзаца выше
</ul>
Писать так в реальном проекте не стоит — читать и поддерживать такую разметку тяжело, а автодополнение в редакторе всё равно расставит теги за вас. Но сам факт полезный: он объясняет, почему браузер не показывает ошибку на кривой странице, а молча достраивает дерево по своим правилам.
И он же подводит к главному повороту в истории языка. Как только спецификация стала описывать не идеальный документ, а реальное поведение браузеров, смысл выпускать её версиями пропал: поведение меняется постоянно, а нумерация предполагает, что в какой-то момент всё замирает.
Почему HTML5 — последняя версия с номером
Официальный релиз состоялся 28 октября 2014 года. Потом вышли редакции 5.1 и 5.2 — и на этом нумерация остановилась. В 2019 году W3C и WHATWG договорились, что единственной действующей редакцией считается HTML Living Standard: спецификация без номера версии, в которую правки вносятся по мере готовности.
На практике это значит вот что: спрашивать «поддерживает ли браузер HTML5» бессмысленно, потому что HTML5 — не набор возможностей, который включается целиком. Поддержка проверяется по каждой конкретной фиче отдельно, и результат у разных фич разный. Единственное, что осталось от нумерации в реальном коде, — строчка доктайпа:
<!DOCTYPE html>
В четвёртой версии доктайп был длинной ссылкой на DTD-схему, которую никто не помнил наизусть и все копировали. Теперь это просто сигнал браузеру «рендери по актуальным правилам, а не в режиме совместимости со старыми страницами». Забыть его — получить quirks mode, в котором ломаются размеры блоков и вертикальные отступы.
Семантика: разметка, которая объясняет структуру
Главное, что принёс HTML5, — набор элементов, по которым видно назначение блока, а не только его положение на экране. До этого вся страница собиралась из <div> с классами вида header, sidebar, content: человек по классам всё понимал, браузер — нет.
<body>
<header>
<nav>...</nav>
</header>
<main>
<article>
<h1>Заголовок материала</h1>
<section>...</section>
</article>
<aside>Блок «читайте также»</aside>
</main>
<footer>...</footer>
</body>
Разница не косметическая. Браузер строит из разметки дерево доступности — параллельную структуру, где у блоков есть роли. Скринридер (программа, которая озвучивает страницу незрячему пользователю) читает именно её и умеет прыгать по ориентирам: шапка, навигация, основное содержимое, подвал. Из <div> ориентира не получится ни при каких классах.
Отдельно стоит <main>. Это единственный элемент, который на странице должен быть в одном экземпляре: он отмечает то самое содержимое, ради которого страницу открыли, без шапки, меню и подвала. Именно на него ведёт ссылка «перейти к содержимому» в начале страницы — приём, без которого клавиатурному пользователю приходится прощёлкивать всё меню на каждой странице (подробнее — в статье про вёрстку под клавиатурную навигацию).
Разделение между <article> и <section> — отдельный сюжет, который стабильно вызывает споры на код-ревью; ему посвящён отдельный разбор. А если незнакомых имён тегов пока слишком много, помогает таблица расшифровок: почти все они — сокращения от английских слов, и после расшифровки запоминаются сами.

Список семантических элементов не заморожен 2014-м годом. Например, для блока поиска появился отдельный элемент <search> — обёртка вокруг формы поиска, которая даёт блоку роль ориентира «поиск»:
<search>
<form action="/search">
<label for="q">Поиск по сайту</label>
<input id="q" name="q" type="search">
<button type="submit">Найти</button>
</form>
</search>
Мультимедиа и графика без плагинов
До HTML5 видео на странице означало Flash: сторонний плагин, который пользователь ставил отдельно, а браузер регулярно блокировал за дыры в безопасности. Пятая версия дала новые элементы, которые работают из коробки. Один из них:
<video controls width="640" poster="/media/preview.jpg">
<source src="/media/lesson.webm" type="video/webm">
<source src="/media/lesson.mp4" type="video/mp4">
<track kind="subtitles" src="/media/lesson.vtt" srclang="uk" label="Субтитры" default>
Браузер не умеет воспроизводить видео. <a href="/media/lesson.mp4">Скачать файл</a>
</video>
Здесь три детали, о которых в подборках «фактов про HTML5» обычно не пишут, а они и решают, заработает ли плеер.
Первая — несколько <source>. Сам HTML не описывает, какие форматы обязан понимать браузер: список кодеков остаётся на усмотрение производителя и зависит ещё и от лицензий. Поэтому один и тот же ролик кладут в двух контейнерах, а браузер берёт первый, который умеет открыть.
Вторая — <track>. Это дорожка субтитров в формате WebVTT, обычный текстовый файл с таймкодами. Без неё видео недоступно тем, кто не слышит, и вдобавок бесполезно для поиска по содержимому ролика. Атрибут default включает дорожку сразу.
Третья — текст между открывающим и закрывающим тегами. Он не отображается, пока плеер работает, и показывается только если браузер элемент не понял. Место для ссылки на скачивание, а не для фразы «обновите браузер».
Рядом с плеерами HTML5 принёс <canvas> — прямоугольник, в котором рисуют из JavaScript попиксельно. На нём держатся браузерные игры, графики, редакторы изображений и генераторы аватарок. Важно понимать ограничение: нарисованное на холсте — это картинка, а не элементы. В дереве документа её содержимого нет, скринридер там ничего не найдёт, мышью не выделишь. Для интерфейса это плохой выбор, для графики — хороший.
Что HTML5 принёс, кроме тегов
Вторая половина «пятёрки» — это не разметка, а браузерные API: функции, которые появились у страницы вместе с новым стандартом. Три из них встречаются чаще прочих.
Хранилище на клиенте
До HTML5 сохранить что-то в браузере можно было только через cookie: четыре килобайта, которые вдобавок улетают на сервер с каждым запросом. Появились два простых хранилища:
// переживёт закрытие браузера
localStorage.setItem('theme', 'dark');
// умрёт вместе со вкладкой
sessionStorage.setItem('step', '3');
const theme = localStorage.getItem('theme'); // 'dark'
Два подвоха, на которые наступают все. Первый: хранилище содержит только строки, поэтому объект придётся прогонять через JSON.stringify при записи и JSON.parse при чтении. Второй: sessionStorage живёт в пределах одной вкладки — открыл ту же страницу в соседней, и там пусто.
Оба хранилища доступны любому скрипту на странице, включая сторонние, так что токены и персональные данные туда класть не стоит. Чем ещё их дополняют и когда брать IndexedDB — в статье про типы хранилищ.
Работа без сети
А вот здесь «фишка HTML5» успела полностью умереть, хотя её до сих пор упоминают в подборках. Офлайн в 2014-м делали через Application Cache: атрибут manifest на теге <html> и текстовый файл со списком того, что сохранить. Механизм оказался неисправимо кривым — страница могла намертво залипнуть на старой версии, — и его выпилили: Firefox избавился от него в 2020 году, Chrome окончательно удалил в 95-й версии. Разбор причин остался в материале web.dev.
Сейчас офлайн делают через service worker — отдельный скрипт, который живёт рядом со страницей и перехватывает её запросы к сети:
// в основном скрипте страницы
navigator.serviceWorker.register('/sw.js');
// в sw.js: отдаём из кэша, если сети нет
self.addEventListener('fetch', (event) => {
event.respondWith(
fetch(event.request).catch(() => caches.match(event.request))
);
});
Разница принципиальная: в старом манифесте правила задавались списком, в service worker их пишут кодом, поэтому обновлением кэша можно управлять, а не надеяться.
Геолокация
Geolocation API умеет спросить у устройства координаты — по GPS, по вышкам связи или по адресу в сети:
navigator.geolocation.getCurrentPosition(
(pos) => console.log(pos.coords.latitude, pos.coords.longitude),
(err) => console.warn('Отказано или недоступно:', err.message)
);
Два условия, без которых вызов не сработает. Первое: браузер обязательно спросит разрешение у пользователя, и отказ — штатный сценарий, который нужно обработать вторым коллбэком. Второе: функция работает только в безопасном контексте — по HTTPS или на localhost. На странице, открытой по обычному HTTP, её просто нет.
Из той же волны браузерных возможностей выросло много вещей, которые сегодня выглядят как часть языка: перетаскивание файлов в окно, чтение выбранного файла без загрузки на сервер, разворот элемента на весь экран, фоновые вычисления в отдельном потоке.
Три привычки из нулевых, которые HTML5 не отменял
В подборках советов по HTML кочуют приёмы, которые формально работают, а по факту создают проблемы. Три самых живучих.
Ссылка-заглушка на решётку
Совет «нужна кнопка — сделай ссылку с решёткой» выглядит безобидно:
<!-- так не надо -->
<a href="#" onclick="openMenu()">Меню</a>
<!-- так надо -->
<button type="button">Меню</button>
Что не так: такая ссылка прокручивает страницу наверх, дописывает решётку в адресную строку и попадает в историю браузера, так что кнопка «назад» начинает возвращать пользователя на ту же страницу. Плюс скринридер объявит её ссылкой — пользователь ждёт перехода, а получает раскрытое меню. Элемент <button> бесплатно даёт фокус, нажатие пробелом и правильную роль. Родственный приём с протоколом-заглушкой в атрибуте разобран в статье про javascript:void(0).
<div> как универсальный контейнер
Формулировка «div — ящик с инструментами, в него можно сложить что угодно» верна технически и вредна практически. Этот элемент не несёт никакого смысла — ровно поэтому он и остаётся правильным выбором, когда блок нужен только для сетки или отступов. Но если у блока есть роль на странице, для неё почти всегда уже есть тег: список — <ul>, раскрывающийся блок — <details>, подпись к иллюстрации — <figcaption>. Дешёвая проверка: если вы дали блоку класс, называющий его назначение, посмотрите, нет ли тега с тем же именем.
Картинка внутри ссылки без подписи
Обернуть изображение в ссылку — нормальный приём: так делают карточки товаров, логотипы и превью статей. Но есть нюанс, про который забывают:
<!-- ссылка без имени: скринридер прочитает адрес файла -->
<a href="/catalog"><img src="/img/promo.png" alt=""></a>
<!-- alt становится именем ссылки -->
<a href="/catalog"><img src="/img/promo.png" alt="Каталог курсов"></a>
Когда в ссылке нет текста, её именем становится alt картинки. Пустой alt уместен для декоративного изображения, но ссылка-картинка декоративной не бывает: пользователь услышит имя файла или просто слово «ссылка». Описывайте в alt не картинку, а то, куда ведёт клик.
Что HTML получил уже после пятёрки
Раз спецификация обновляется непрерывно, у неё нет «шестой версии», зато есть готовые элементы, которые закрывают задачи, раньше требовавшие библиотек.
Нативное модальное окно
Элемент <dialog> сам забирает фокус, запирает его внутри окна, закрывается по Esc и рисует затемнение фона через псевдоэлемент ::backdrop:
<dialog id="delete-draft">
<form method="dialog">
<p>Удалить черновик?</p>
<button value="cancel">Отмена</button>
<button value="ok">Удалить</button>
</form>
</dialog>
<script>
document.getElementById('delete-draft').showModal();
</script>
Форма с атрибутом method="dialog" закрывает окно при отправке и кладёт значение нажатой кнопки в свойство returnValue — удобно, чтобы отличить подтверждение от отмены без дополнительных обработчиков.
Аккордеон без единой строчки скрипта
Раскрывающийся блок <details> существует давно, а вот атрибут name у него — новинка. Блоки с одинаковым именем начинают вести себя как радиокнопки: открыли один — соседний закрылся сам.
<details name="faq" open>
<summary>Как оплатить курс?</summary>
<p>Картой или переводом, счёт приходит на почту.</p>
</details>
<details name="faq">
<summary>Есть ли рассрочка?</summary>
<p>Да, на три месяца без процентов.</p>
</details>
Всплывающие слои поверх страницы
Атрибут popover превращает любой элемент в слой, который всплывает над остальным содержимым, закрывается по Esc и по клику мимо — и при этом не зависит от overflow и z-index родителей, потому что рисуется в отдельном верхнем слое:
<button popovertarget="hint">Что это значит?</button>
<div id="hint" popover>
<p>Код города без ведущего нуля, например 44 для Kyiv.</p>
</div>
Разница с модальным окном простая: попап не запирает фокус и не блокирует страницу, поэтому он для подсказок, меню и тостов, а <dialog> — для вопросов, на которые нужно ответить.
Кнопки, которые управляют элементами без обработчиков
Самое свежее пополнение — пара атрибутов command и commandfor у кнопки. Первый говорит, что сделать, второй — с каким элементом. Строчка с showModal из примера выше становится не нужна:
<button command="show-modal" commandfor="delete-draft">Удалить черновик</button>
<dialog id="delete-draft">
<p>Удалить черновик?</p>
<button command="close" commandfor="delete-draft">Отмена</button>
</dialog>
Набор команд пока небольшой — открыть и закрыть диалог, переключить попап, — но направление понятное: типовые действия переезжают из скриптов в разметку. Механизм получил статус Baseline newly available в декабре 2025 года, так что в продакшене к нему пока нужен запасной обработчик на клик.
Итого
HTML5 стоит воспринимать не как версию, а как момент, когда язык разметки перестал описывать только внешний вид документа и начал описывать его смысл — плюс обзавёлся встроенным плеером, хранилищем и доступом к возможностям устройства. Номер на этом закончился: дальше спецификация просто дописывается, и <search>, <dialog> или popover — такой же HTML, как <header> из 2014 года.
Практический вывод один: проверять надо не «поддержку HTML5», а поддержку конкретного элемента или атрибута — и не переносить в новый код приёмы десятилетней давности только потому, что они встретились в очередной подборке советов.
Комментарии (0)