Ты уже знаешь все основные теги и концепции HTML. Теперь поговорим о том, что отличает профессионала от новичка: правильная структура, именование, форматирование и валидация. Хороший HTML — это не просто «работает», это «работает, понятно и поддерживается».
Каждый HTML-документ должен начинаться с корректной базовой структуры:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Описательный заголовок страницы</title>
</head>
<body>
<!-- Контент -->
</body>
</html><!-- ПЛОХО: нарушена вложенность -->
<p>Текст <strong>жирный <em>жирный-курсив</strong> курсив</em></p>
<!-- ХОРОШО: теги закрываются в обратном порядке -->
<p>Текст <strong>жирный <em>жирный-курсив</em></strong> <em>курсив</em></p>HTML — это дерево. Каждый открытый тег должен быть закрыт до того, как закроется его родитель.
<!-- ПЛОХО: заглавные теги -->
<DIV CLASS="header">
<H1>Заголовок</H1>
</DIV>
<!-- ХОРОШО: строчные теги -->
<div class="header">
<h1>Заголовок</h1>
</div>HTML нечувствителен к регистру, но конвенция — всегда строчные.
Теги без содержимого (void elements) должны правильно закрываться:
<!-- В HTML5 оба варианта корректны, но <tag /> предпочтительнее для JSX -->
<img src="photo.jpg" alt="Фото" />
<br />
<hr />
<input type="text" />
<meta charset="UTF-8" /><!-- ПЛОХО: без кавычек или одинарные кавычки -->
<img src=photo.jpg alt=Фото />
<a href='https://example.com'>Ссылка</a>
<!-- ХОРОШО: двойные кавычки -->
<img src="photo.jpg" alt="Фото" />
<a href="https://example.com">Ссылка</a><!-- ПЛОХО: непонятные имена -->
<div class="d1 red big">...</div>
<div id="x">...</div>
<!-- ХОРОШО: имена описывают назначение, не внешний вид -->
<div class="product-card featured">...</div>
<div id="checkout-modal">...</div>Используй kebab-case для классов и id. Называй по назначению, а не по виду: не red-text, а error-message.
<!-- ПЛОХО: всё в одну строку -->
<div class="card"><img src="prod.jpg" alt="Товар" /><h3>Nike Air Max</h3><p class="price">7 990 ₽</p><button>Купить</button></div>
<!-- ХОРОШО: отступы 2 пробела, каждый элемент на своей строке -->
<div class="product-card">
<img src="product.jpg" alt="Кроссовки Nike Air Max 90" />
<h3 class="product-title">Nike Air Max 90</h3>
<p class="product-price">7 990 ₽</p>
<button class="btn btn-primary" type="button">Купить</button>
</div><!-- Начало секции каталога -->
<section class="catalog">
...
</section>
<!-- /Конец секции каталога -->
<!-- Временно скрыто до запуска -->
<!-- <div class="promo-banner">...</div> -->Комментарии полезны для разделения больших секций. Не комментируй очевидные вещи.
<!-- ПЛОХО: используем теги для стилизации, не для семантики -->
<b>Важный текст</b> <!-- b = просто жирный -->
<i>Иностранное слово</i> <!-- i = просто курсив -->
<br /><br /> <!-- Отступы через br — нет! -->
<!-- ХОРОШО: теги по смыслу, отступы через CSS -->
<strong>Важный текст</strong> <!-- strong = важность -->
<em>Иностранное слово</em> <!-- em = акцент -->Инструмент: validator.w3.org — официальный валидатор W3C. Вставь URL или HTML-код и получи список ошибок. В реальных проектах валидацию интегрируют в CI/CD.
В командах используют линтеры для HTML: HTMLHint, html-validate. Prettier форматирует HTML автоматически. Обзор кода (code review) всегда включает проверку HTML-качества. Хороший HTML — это уважение к коллегам, поисковикам и пользователям.
Генерация и форматирование валидного HTML документа
// Генерируем корректный HTML-документ программно
function createHTMLDocument(config) {
const lines = []
lines.push('<!DOCTYPE html>')
lines.push('<html lang="' + (config.lang || 'ru') + '">')
lines.push(' <head>')
lines.push(' <meta charset="UTF-8" />')
lines.push(' <meta name="viewport" content="width=device-width, initial-scale=1.0" />')
lines.push(' <title>' + config.title + '</title>')
if (config.description) {
lines.push(' <meta name="description" content="' + config.description + '" />')
}
lines.push(' </head>')
lines.push(' <body>')
lines.push(' <header>')
lines.push(' <nav>')
lines.push(' <ul>')
config.navLinks.forEach(link => {
lines.push(' <li><a href="' + link.href + '">' + link.text + '</a></li>')
})
lines.push(' </ul>')
lines.push(' </nav>')
lines.push(' </header>')
lines.push(' <main>')
lines.push(' <h1>' + config.h1 + '</h1>')
lines.push(' </main>')
lines.push(' <footer>')
lines.push(' <p>' + config.footer + '</p>')
lines.push(' </footer>')
lines.push(' </body>')
lines.push('</html>')
return lines
}
const html = createHTMLDocument({
lang: 'ru',
title: 'Sneaker Shop — Кроссовки онлайн',
description: 'Лучший магазин кроссовок',
h1: 'Каталог кроссовок',
navLinks: [
{ href: '/', text: 'Главная' },
{ href: '/catalog', text: 'Каталог' },
{ href: '/about', text: 'О нас' },
],
footer: '© 2024 Sneaker Shop',
})
html.forEach(line => console.log(line))
console.log('Строк в документе:', html.length)HTML-линтер — проверка типичных ошибок
// Простой HTML-линтер (проверяем правила через строки)
function lintHTML(htmlString) {
const errors = []
const warnings = []
// Проверка DOCTYPE
if (!htmlString.includes('<!DOCTYPE html>')) {
errors.push('Нет <!DOCTYPE html>')
}
// Проверка lang
if (!htmlString.includes('lang=')) {
errors.push('Нет атрибута lang у <html>')
}
// Проверка charset
if (!htmlString.includes('charset=')) {
errors.push('Нет meta charset')
}
// Проверка title
if (!htmlString.includes('<title>')) {
errors.push('Нет тега <title>')
}
// Проверка заглавных тегов
if (/<[A-Z]/.test(htmlString)) {
warnings.push('Найдены теги в верхнем регистре — используй строчные')
}
// Проверка атрибутов без кавычек
if (/=w+[s>]/.test(htmlString.replace(/="[^"]*"/g, ''))) {
warnings.push('Возможны атрибуты без кавычек')
}
// Проверка img без alt
const imgWithoutAlt = (htmlString.match(/<img(?![^>]*alt=)[^>]*>/g) || []).length
if (imgWithoutAlt > 0) {
errors.push('Найдено ' + imgWithoutAlt + ' тег(ов) img без alt')
}
return { errors, warnings }
}
const badHTML = `<HTML>
<head>
<title>Мой сайт</title>
</head>
<body>
<IMG src="photo.jpg" />
<div class=wrapper>Контент</div>
</body>
</HTML>`
const result = lintHTML(badHTML)
console.log('Ошибок:', result.errors.length)
result.errors.forEach(e => console.log('ОШИБКА: ' + e))
console.log('Предупреждений:', result.warnings.length)
result.warnings.forEach(w => console.log('ПРЕДУПРЕЖДЕНИЕ: ' + w))Ты уже знаешь все основные теги и концепции HTML. Теперь поговорим о том, что отличает профессионала от новичка: правильная структура, именование, форматирование и валидация. Хороший HTML — это не просто «работает», это «работает, понятно и поддерживается».
Каждый HTML-документ должен начинаться с корректной базовой структуры:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Описательный заголовок страницы</title>
</head>
<body>
<!-- Контент -->
</body>
</html><!-- ПЛОХО: нарушена вложенность -->
<p>Текст <strong>жирный <em>жирный-курсив</strong> курсив</em></p>
<!-- ХОРОШО: теги закрываются в обратном порядке -->
<p>Текст <strong>жирный <em>жирный-курсив</em></strong> <em>курсив</em></p>HTML — это дерево. Каждый открытый тег должен быть закрыт до того, как закроется его родитель.
<!-- ПЛОХО: заглавные теги -->
<DIV CLASS="header">
<H1>Заголовок</H1>
</DIV>
<!-- ХОРОШО: строчные теги -->
<div class="header">
<h1>Заголовок</h1>
</div>HTML нечувствителен к регистру, но конвенция — всегда строчные.
Теги без содержимого (void elements) должны правильно закрываться:
<!-- В HTML5 оба варианта корректны, но <tag /> предпочтительнее для JSX -->
<img src="photo.jpg" alt="Фото" />
<br />
<hr />
<input type="text" />
<meta charset="UTF-8" /><!-- ПЛОХО: без кавычек или одинарные кавычки -->
<img src=photo.jpg alt=Фото />
<a href='https://example.com'>Ссылка</a>
<!-- ХОРОШО: двойные кавычки -->
<img src="photo.jpg" alt="Фото" />
<a href="https://example.com">Ссылка</a><!-- ПЛОХО: непонятные имена -->
<div class="d1 red big">...</div>
<div id="x">...</div>
<!-- ХОРОШО: имена описывают назначение, не внешний вид -->
<div class="product-card featured">...</div>
<div id="checkout-modal">...</div>Используй kebab-case для классов и id. Называй по назначению, а не по виду: не red-text, а error-message.
<!-- ПЛОХО: всё в одну строку -->
<div class="card"><img src="prod.jpg" alt="Товар" /><h3>Nike Air Max</h3><p class="price">7 990 ₽</p><button>Купить</button></div>
<!-- ХОРОШО: отступы 2 пробела, каждый элемент на своей строке -->
<div class="product-card">
<img src="product.jpg" alt="Кроссовки Nike Air Max 90" />
<h3 class="product-title">Nike Air Max 90</h3>
<p class="product-price">7 990 ₽</p>
<button class="btn btn-primary" type="button">Купить</button>
</div><!-- Начало секции каталога -->
<section class="catalog">
...
</section>
<!-- /Конец секции каталога -->
<!-- Временно скрыто до запуска -->
<!-- <div class="promo-banner">...</div> -->Комментарии полезны для разделения больших секций. Не комментируй очевидные вещи.
<!-- ПЛОХО: используем теги для стилизации, не для семантики -->
<b>Важный текст</b> <!-- b = просто жирный -->
<i>Иностранное слово</i> <!-- i = просто курсив -->
<br /><br /> <!-- Отступы через br — нет! -->
<!-- ХОРОШО: теги по смыслу, отступы через CSS -->
<strong>Важный текст</strong> <!-- strong = важность -->
<em>Иностранное слово</em> <!-- em = акцент -->Инструмент: validator.w3.org — официальный валидатор W3C. Вставь URL или HTML-код и получи список ошибок. В реальных проектах валидацию интегрируют в CI/CD.
В командах используют линтеры для HTML: HTMLHint, html-validate. Prettier форматирует HTML автоматически. Обзор кода (code review) всегда включает проверку HTML-качества. Хороший HTML — это уважение к коллегам, поисковикам и пользователям.
Генерация и форматирование валидного HTML документа
// Генерируем корректный HTML-документ программно
function createHTMLDocument(config) {
const lines = []
lines.push('<!DOCTYPE html>')
lines.push('<html lang="' + (config.lang || 'ru') + '">')
lines.push(' <head>')
lines.push(' <meta charset="UTF-8" />')
lines.push(' <meta name="viewport" content="width=device-width, initial-scale=1.0" />')
lines.push(' <title>' + config.title + '</title>')
if (config.description) {
lines.push(' <meta name="description" content="' + config.description + '" />')
}
lines.push(' </head>')
lines.push(' <body>')
lines.push(' <header>')
lines.push(' <nav>')
lines.push(' <ul>')
config.navLinks.forEach(link => {
lines.push(' <li><a href="' + link.href + '">' + link.text + '</a></li>')
})
lines.push(' </ul>')
lines.push(' </nav>')
lines.push(' </header>')
lines.push(' <main>')
lines.push(' <h1>' + config.h1 + '</h1>')
lines.push(' </main>')
lines.push(' <footer>')
lines.push(' <p>' + config.footer + '</p>')
lines.push(' </footer>')
lines.push(' </body>')
lines.push('</html>')
return lines
}
const html = createHTMLDocument({
lang: 'ru',
title: 'Sneaker Shop — Кроссовки онлайн',
description: 'Лучший магазин кроссовок',
h1: 'Каталог кроссовок',
navLinks: [
{ href: '/', text: 'Главная' },
{ href: '/catalog', text: 'Каталог' },
{ href: '/about', text: 'О нас' },
],
footer: '© 2024 Sneaker Shop',
})
html.forEach(line => console.log(line))
console.log('Строк в документе:', html.length)HTML-линтер — проверка типичных ошибок
// Простой HTML-линтер (проверяем правила через строки)
function lintHTML(htmlString) {
const errors = []
const warnings = []
// Проверка DOCTYPE
if (!htmlString.includes('<!DOCTYPE html>')) {
errors.push('Нет <!DOCTYPE html>')
}
// Проверка lang
if (!htmlString.includes('lang=')) {
errors.push('Нет атрибута lang у <html>')
}
// Проверка charset
if (!htmlString.includes('charset=')) {
errors.push('Нет meta charset')
}
// Проверка title
if (!htmlString.includes('<title>')) {
errors.push('Нет тега <title>')
}
// Проверка заглавных тегов
if (/<[A-Z]/.test(htmlString)) {
warnings.push('Найдены теги в верхнем регистре — используй строчные')
}
// Проверка атрибутов без кавычек
if (/=w+[s>]/.test(htmlString.replace(/="[^"]*"/g, ''))) {
warnings.push('Возможны атрибуты без кавычек')
}
// Проверка img без alt
const imgWithoutAlt = (htmlString.match(/<img(?![^>]*alt=)[^>]*>/g) || []).length
if (imgWithoutAlt > 0) {
errors.push('Найдено ' + imgWithoutAlt + ' тег(ов) img без alt')
}
return { errors, warnings }
}
const badHTML = `<HTML>
<head>
<title>Мой сайт</title>
</head>
<body>
<IMG src="photo.jpg" />
<div class=wrapper>Контент</div>
</body>
</HTML>`
const result = lintHTML(badHTML)
console.log('Ошибок:', result.errors.length)
result.errors.forEach(e => console.log('ОШИБКА: ' + e))
console.log('Предупреждений:', result.warnings.length)
result.warnings.forEach(w => console.log('ПРЕДУПРЕЖДЕНИЕ: ' + w))Напиши валидный HTML-документ по всем правилам хорошего HTML: правильная структура с DOCTYPE, lang, charset, viewport, уникальный title до 60 символов, h1 на странице один, все img с alt, теги строчные, атрибуты в двойных кавычках, классы в kebab-case.
lang="ru" для русскоязычного сайта. charset="UTF-8". viewport: width=device-width, initial-scale=1.0. Один h1 на страницу. img всегда с alt — пустой alt="" для декоративных. Теги строчные: <header>, <main>, <footer>. Классы в kebab-case: page-container, site-header, site-footer.