Невалидный HTML: какие ошибки браузер прощает, а какие стоят денег
Дубли id, незакрытые теги, вложенные формы. Ошибки разметки, которые живут, потому что браузер их чинит, и те, что тихо ломают поведение.
HTML · Опубликовано: 8 сентября 2026 г. · 3 мин чтения · Марк Лебедев
Браузеры прощают по устройству: у HTML-парсера есть описанная процедура восстановления практически для любого кривого ввода. Из-за этой снисходительности невалидная разметка живёт годами — и часть её при этом наносит реальный ущерб, которого никто не замечает.
Ошибки, которые действительно ломают
Дубли id. document.getElementById возвращает первое совпадение и только его. Подпись указывает не на то поле; скрипт вешает обработчик на элемент, по которому никто не кликает. Это самая частая находка на компонентных сайтах: id, зашитый внутри компонента, становится дублем в ту секунду, когда компонент отрисовался дважды.
Вложенные формы. HTML их не допускает, и парсер молча выбрасывает внутреннюю. Кнопка отправки внутри неё перестаёт работать, а код в редакторе выглядит правильным.
Label без цели. for="email" при отсутствии id="email" ломает клик по подписи и лишает поле доступного имени — одна ошибка, которая стоит и удобства, и доступности.
Интерактивные элементы, вложенные друг в друга. Кнопка внутри ссылки или ссылка внутри кнопки дают разное поведение в разных браузерах и непредсказуемый порядок фокуса.
Дублирующиеся атрибуты. <input value="a" value="b"> — второй отбрасывается, и обычно это тот, который кто-то добавил позже, чиня баг.
Ошибки, которые в основном косметические
Незакрытый <li>, одинокий & вместо &, устаревший <center>, отсутствующий type у скрипта. Парсер справляется со всеми, и отрисованная страница идентична. Их стоит чинить, когда это дёшево, но спринта они не заслуживают.
Одна оговорка: незакрытые теги в таблицах и списках меняют форму DOM-дерева, а не только текст разметки, — то есть двигают контент на странице. Такие считайте настоящими.
Почему линтера исходников недостаточно
Шаблоны собираются в рантайме. Компонент, валидный сам по себе, становится невалидным на странице, где он выведен дважды или обёрнут в уже открытую форму. Линтер читает файлы; браузер видит собранный документ. Единственная надёжная проверка — по отрендеренному HTML реальных страниц:
для каждой обойдённой страницы:
разобрать отданный HTML
есть ли id, встречающиеся больше раза?
есть ли формы внутри форм?
есть ли label[for] без соответствующего id?
есть ли интерактивные элементы внутри интерактивных?
Запускайте по всему сайту и группируйте по шаблонам — как и с большинством проблем разметки, один плохой компонент объясняет сотню плохих страниц.
Чего стоит пропустить это
Ничего драматичного — в этом и беда. Форма, которая не отправляется на одном шаблоне. Скринридер, называющий не то поле. Обработчик, повешенный на невидимый дубль. Каждое выглядит отдельным багом, получает задачу, получает костыль — а настоящая причина, три строки разметки, остаётся на месте и готовит следующий «отдельный баг».
Читайте также
Проверьте, что из этого есть на вашем сайте
Бесплатное сканирование покажет битые ссылки, ошибки JS и проблемы производительности за минуту.