Skip to content

HTML Шпаргалка

Стандартный язык разметки для создания веб-страниц.

01

Структура документа

Базовый шаблон страницы

Каждый HTML5-документ начинается с <!DOCTYPE html>, за которым следует <html> с атрибутом lang для доступности и SEO. <head> содержит метаданные (charset, viewport, title), а <body> хранит видимый контент. Мета-тег viewport обеспечивает корректный рендеринг на мобильных устройствах.

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My Web Page</title>
</head>
<body>
  <h1>Hello, World!</h1>
  <p>Welcome to my page.</p>
</body>
</html>

Раздел Head и метаданные

Элемент <head> содержит машиночитаемые метаданные. charset объявляет кодировку символов (UTF-8 покрывает почти все символы). Мета-тег description критически важен для SEO — поисковые системы отображают его в результатах. Используйте defer на скриптах, чтобы загружать их после парсинга HTML.

html
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Page Title - Site Name</title>
  <meta name="description" content="A brief description for SEO">
  <meta name="author" content="John Doe">
  <link rel="stylesheet" href="style.css">
  <script src="app.js" defer></script>
</head>

Комментарии и условные комментарии

HTML-комментарии начинаются с <!-- и заканчиваются -->. Они не отображаются в браузере, но видны в исходном коде страницы. Комментарии полезны для заметок, разметки секций (TODO, FIXME) и документации. Условные комментарии (только для IE) устарели, но могут встречаться в legacy-коде.

html
<!-- This is a single-line comment -->

<!--
  This is a
  multi-line comment
-->

<!-- TODO: Add form validation -->
<!-- FIXME: Fix layout on mobile -->

<!--[if lt IE 9]>
  <script src="html5shiv.js"></script>
<![endif]-->

Иерархия заголовков

HTML предоставляет шесть уровней заголовков, от h1 (высший) до h6 (низший). Используйте их в иерархическом порядке без пропуска уровней. Поисковые системы используют заголовки для понимания структуры страницы. Лучше всего: один h1 на страницу, затем h2 для основных секций, h3 для подсекций и т.д.

html
<h1>Main Page Title</h1>
<h2>Major Section</h2>
<h3>Subsection</h3>
<h4>Sub-subsection</h4>
<h5>Minor Heading</h5>
<h6>Lowest Level Heading</h6>

<!-- Use only one h1 per page for SEO -->
<h1>Blog Post Title</h1>
<h2>Introduction</h2>
<h3>Background</h3>

Контейнеры Div и Span

<div> — блочный контейнер для группировки элементов и применения стилей. <span> — строчный контейнер для стилизации небольших частей текста. Оба — универсальные элементы без семантического значения — предпочитайте семантические теги (header, nav, main, article), когда уместно, для лучшей доступности и SEO.

html
<div class="container">
  <div id="header" class="header">
    <span class="logo">MySite</span>
    <span class="tagline">Best Content</span>
  </div>
  <div class="content">
    <p>Main <span class="highlight">content</span> here.</p>
  </div>
</div>
02

Форматирование текста

Жирный, курсив и выделение

<strong> указывает важный текст (скринридеры выделяют его), тогда как <b> — чисто визуальное жирное начертание. Аналогично, <em> указывает на смысловое ударение (семантическое), тогда как <i> — визуальный курсив. Предпочитайте <strong> и <em> для доступности. <b> и <i> допустимы для стилистических целей без семантической важности.

html
<p>This is <strong>important</strong> text.</p>
<p>This is <b>bold</b> text.</p>
<p>This is <em>emphasized</em> text.</p>
<p>This is <i>italic</i> text.</p>
<p><strong>Warning:</strong> Do not touch!</p>

Mark, Delete и Insert

<mark> подсвечивает текст как релевантный (как подсветка поиска). <del> помечает удалённый текст, <ins> — вставленный — полезно для отображения правок. <s> зачёркивает устаревшую информацию. <small> — для побочных комментариев или копирайта. <u> следует использовать осторожно, так как он напоминает ссылки.

html
<p>Please <mark>highlight this</mark> word.</p>
<p>The price is <del>$99</del> <ins>$79</ins> now.</p>
<p><s>Old information</s> is struck through.</p>
<p>Use <u>underline</u> sparingly.</p>
<p>Small <small>print</small> for side comments.</p>

Верхний и нижний индексы

<sub> создаёт подстрочный текст (ниже базовой линии) для химических формул (H2O) и математических переменных. <sup> создаёт надстрочный текст (выше базовой линии) для степеней (x²), порядковых чисел (1st) и сносок. Оба — строчные элементы, корректирующие вертикальное положение и размер шрифта.

html
<p>H<sub>2</sub>O is water.</p>
<p>E = mc<sup>2</sup> is Einstein's equation.</p>
<p>1st<sup>st</sup> January 2024</p>
<p>CO<sub>2</sub> emissions are rising.</p>
<p>x<sup>2</sup> + y<sup>2</sup> = r<sup>2</sup></p>

Цитаты и ссылки на источники

<blockquote> — для блочных цитат (с отступом, многострочных). <q> — для строчных коротких цитат (автоматически добавляет кавычки). Атрибут cite предоставляет URL источника. <cite> помечает название произведения или имя автора. Эти элементы улучшают семантическую разметку цитируемого контента.

html
<blockquote cite="https://example.com/source">
  <p>The best way to predict the future is to invent it.</p>
  <footer>— <cite>Alan Kay</cite></footer>
</blockquote>

<p>As <q cite="https://example.com">someone once said</q>,
knowledge is power.</p>

Код и предварительно форматированный текст

<code> помечает строчные фрагменты кода (моноширинный шрифт). <pre> сохраняет пробелы и переносы строк для предварительно форматированного текста. Комбинация <pre><code> отображает блоки кода с правильными отступами. <kbd> представляет ввод с клавиатуры. <samp> — для вывода программы, а <var> — для переменных в математических выражениях.

html
<p>Use the <code>console.log()</code> function.</p>
<p>Press <kbd>Ctrl</kbd> + <kbd>C</kbd> to copy.</p>

<pre><code>function hello() {
  console.log("Hello, World!");
  return true;
}</code></pre>

Переносы строк и горизонтальные линии

<br> создаёт перенос строки в тексте (используйте умеренно, предпочитайте CSS). <hr> создаёт горизонтальную линию, представляющую тематический разрыв между секциями. <wbr> предлагает возможность переноса строки для длинных слов или URL, позволяя браузеру разрывать в оптимальных точках. Избегайте использования <br> для интервалов — используйте CSS margins.

html
<p>First line<br>Second line<br>Third line</p>

<p>Paragraph one.</p>
<hr>
<p>Paragraph two after a thematic break.</p>

<wbr><!-- word break opportunity for long URLs -->
https://example.com/<wbr>very<wbr>long<wbr>url
04

Изображения и мультимедиа

Изображения

Всегда включайте alt-текст для доступности и SEO — он описывает изображение для скринридеров и когда изображение не загружается. loading='lazy' откладывает загрузку внеэкранных изображений, улучшая скорость страницы. srcset и sizes обеспечивают адаптивные изображения — браузер выбирает лучшее изображение на основе разрешения устройства и viewport. Всегда указывайте width и height для предотвращения сдвига макета.

html
<!-- Basic image -->
<img src="photo.jpg" alt="A sunset over mountains" width="800" height="600">

<!-- Image with lazy loading -->
<img src="hero.jpg" alt="Hero banner" loading="lazy" decoding="async">

<!-- Responsive image with srcset -->
<img
  src="medium.jpg"
  srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
  sizes="(max-width: 600px) 480px, 800px"
  alt="Responsive photo"
>

Элемент Picture

<picture> обеспечивает art direction и форматный fallback. Элементы <source> предлагают альтернативы — браузер выбирает первый поддерживаемый формат (WebP/AVIF меньше). Атрибут media включает разные изображения для разных размеров экрана. Финальный <img> — fallback и должен всегда присутствовать. Это превосходит srcset для обслуживания полностью разных изображений.

html
<picture>
  <source srcset="webp-image.webp" type="image/webp">
  <source srcset="avif-image.avif" type="image/avif">
  <source srcset="wide.jpg" media="(min-width: 800px)">
  <img src="fallback.jpg" alt="Art-directed image">
</picture>

Аудио

<audio> встраивает звуковой контент. controls добавляет кнопки play/pause/volume. Несколько элементов <source> предоставляют форматные fallback'и (MP3 наиболее широко поддерживается). autoplay ограничен браузерами — muted autoplay обычно разрешён. loop повторяет аудио. Текст внутри отображается, если аудио не поддерживается. Всегда предоставляйте controls для пользовательского опыта.

html
<audio controls>
  <source src="song.mp3" type="audio/mpeg">
  <source src="song.ogg" type="audio/ogg">
  Your browser does not support the audio element.
</audio>

<!-- Autoplay (muted required in most browsers) -->
<audio autoplay muted loop>
  <source src="background.mp3" type="audio/mpeg">
</audio>

Видео

<video> встраивает видео с элементами управления play, pause, volume и fullscreen. poster устанавливает миниатюру перед воспроизведением. Несколько источников предоставляют форматный fallback (MP4/H.264 наиболее совместим). <track> добавляет субтитры, подписи или описания для доступности. playsinline предотвращает принудительный fullscreen на iOS. Всегда включайте controls, если не используете muted autoplay.

html
<video controls width="640" height="360" poster="thumbnail.jpg">
  <source src="movie.mp4" type="video/mp4">
  <source src="movie.webm" type="video/webm">
  <track kind="subtitles" src="subs.vtt" srclang="en" label="English">
  Your browser does not support video.
</video>

<!-- Autoplay muted loop (common for backgrounds) -->
<video autoplay muted loop playsinline>
  <source src="bg.mp4" type="video/mp4">
</video>

Figure и Figcaption

<figure> группирует самодостаточный контент — изображения, диаграммы, листинги кода или цитаты — с их подписью. <figcaption> предоставляет подпись или легенду. Эта семантическая группировка улучшает доступность — скринридеры объявляют отношение. Figure можно ссылаться из текста ('см. Рисунок 1') и перемещать в макете без потери контекста.

html
<figure>
  <img src="chart.png" alt="Quarterly sales chart showing 20% growth">
  <figcaption>Figure 1: Q4 2024 sales growth by region.</figcaption>
</figure>

<figure>
  <blockquote>
    <p>The best way to predict the future is to invent it.</p>
  </blockquote>
  <figcaption>— Alan Kay, 1971</figcaption>
</figure>

Iframe-вставки

<iframe> встраивает внешний контент (видео, карты, другие страницы). Всегда включайте атрибут title для доступности. loading='lazy' улучшает производительность для внеэкранных iframe. Атрибут allow указывает политики функций. Атрибут sandbox ограничивает возможности iframe для безопасности. Будьте осторожны — iframe могут влиять на производительность и безопасность.

html
<!-- Embed a YouTube video -->
<iframe
  src="https://www.youtube.com/embed/dQw4w9WgXcQ"
  width="560" height="315"
  title="YouTube video"
  frameborder="0"
  allow="accelerometer; autoplay; encrypted-media"
  allowfullscreen>
</iframe>

<!-- Embed a map -->
<iframe
  src="https://maps.google.com/maps?q=Paris&output=embed"
  title="Map of Paris" loading="lazy">
</iframe>
05

Списки

Неупорядоченные списки

<ul> создаёт неупорядоченный (маркированный) список. Каждый элемент обёрнут в <li>. CSS-свойство list-style-type изменяет стиль маркеров (disc, circle, square, none). Неупорядоченные списки — для элементов, где порядок не важен. Используйте list-style: none и пользовательскую стилизацию для навигационных меню и списков функций.

html
<ul>
  <li>Apple</li>
  <li>Banana</li>
  <li>Cherry</li>
</ul>

<!-- Custom bullet style via CSS -->
<ul style="list-style-type: square;">
  <li>First item</li>
  <li>Second item</li>
</ul>

Упорядоченные списки

<ol> создаёт упорядоченный (нумерованный) список для последовательных элементов. Атрибут start устанавливает начальный номер. reversed отображает элементы в порядке убывания. Атрибут type изменяет стиль нумерации (1, A, a, I, i). Используйте <ol> для шагов, рейтингов или любой последовательности, где порядок важен.

html
<ol>
  <li>First step</li>
  <li>Second step</li>
  <li>Third step</li>
</ol>

<!-- Start from a specific number -->
<ol start="5">
  <li>Fifth item</li>
  <li>Sixth item</li>
</ol>

<!-- Reverse order -->
<ol reversed>
  <li>Countdown 3</li>
  <li>Countdown 2</li>
  <li>Countdown 1</li>
</ol>

Списки определений

<dl> создаёт список определений, сопоставляя термины (<dt>) с описаниями (<dd>). Идеально для глоссариев, страниц FAQ и пар термин-определение. Несколько <dd> могут описывать один <dt>, и наоборот. Списки определений предоставляют семантическое значение, которого нет у таблиц определений, улучшая доступность.

html
<dl>
  <dt>HTML</dt>
  <dd>HyperText Markup Language</dd>

  <dt>CSS</dt>
  <dd>Cascading Style Sheets</dd>

  <dt>JavaScript</dt>
  <dd>A programming language for the web</dd>
</dl>

Вложенные списки

Списки могут быть вложены путём размещения <ul> или <ol> внутри <li>. Браузеры автоматически делают отступ для вложенных списков и могут менять стили маркеров. Вложение создаёт иерархические структуры вроде оглавлений, файловых деревьев или многоуровневых меню. Держите вложение разумным (2-3 уровня) для удобства. Глубоко вложенные списки трудно читать.

html
<ul>
  <li>Fruits
    <ul>
      <li>Apple</li>
      <li>Banana</li>
    </ul>
  </li>
  <li>Vegetables
    <ol>
      <li>Carrot</li>
      <li>Spinach</li>
    </ol>
  </li>
</ul>

Список со ссылками (навигация)

Списки ссылок внутри <nav> создают семантические навигационные меню. Вложенные элементы <ul> формируют выпадающие подменю. Эта структура доступна для скринридеров и легко стилизуется с CSS. Используйте aria-атрибуты (aria-expanded, aria-haspopup) для интерактивных выпадающих меню. Навигация на основе списков — стандартный шаблон для доступных меню.

html
<nav>
  <ul class="menu">
    <li><a href="/">Home</a></li>
    <li><a href="/products">Products</a>
      <ul class="submenu">
        <li><a href="/products/a">Product A</a></li>
        <li><a href="/products/b">Product B</a></li>
      </ul>
    </li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>
06

Таблицы

Базовая таблица

<table> создаёт табличные данные. <thead> группирует строки заголовков, <tbody> — строки тела, <tfoot> — строки подвала. <tr> — строка таблицы, <th> — ячейка заголовка (жирная, по центру), <td> — ячейка данных. Использование thead/tbody/tfoot улучшает доступность и обеспечивает лучшую стилизацию. Таблицы следует использовать для данных, а не для макета.

html
<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Age</th>
      <th>City</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Alice</td>
      <td>30</td>
      <td>New York</td>
    </tr>
    <tr>
      <td>Bob</td>
      <td>25</td>
      <td>London</td>
    </tr>
  </tbody>
</table>

Объединение ячеек

colspan заставляет ячейку охватывать несколько столбцов (объединить горизонтально). rowspan заставляет ячейку охватывать несколько строк (объединить вертикально). Эти атрибуты создают сложные табличные макеты, например, заголовки, группирующие несколько столбцов. Будьте осторожны с объединением — оно может усложнить парсинг таблиц для скринридеров. Всегда тестируйте доступность.

html
<table border="1">
  <tr>
    <th colspan="2">Name</th>
    <th rowspan="2">Age</th>
  </tr>
  <tr>
    <th>First</th>
    <th>Last</th>
  </tr>
  <tr>
    <td>Alice</td>
    <td>Smith</td>
    <td>30</td>
  </tr>
</table>

Подпись таблицы и scope

<caption> предоставляет заголовок таблицы, улучшая доступность. Атрибут scope на <th> говорит скринридерам, применяется ли заголовок к столбцу (scope='col') или строке (scope='row'). Это необходимо для сложных таблиц. Для ещё более сложных таблиц используйте атрибуты id и headers для явной связи ячеек с их заголовками.

html
<table>
  <caption>Employee Salary Report 2024</caption>
  <thead>
    <tr>
      <th scope="col">Name</th>
      <th scope="col">Department</th>
      <th scope="col">Salary</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Alice</th>
      <td>Engineering</td>
      <td>$95,000</td>
    </tr>
  </tbody>
</table>

Группы столбцов

<colgroup> группирует столбцы для стилизации. Элементы <col> внутри него применяют стили (особенно width) к конкретным столбцам. Это эффективнее, чем установка ширины для каждой ячейки. Группы столбцов также поддерживают атрибут span для стилизации нескольких столбцов сразу. Используйте для согласованной ширины столбцов по всем строкам.

html
<table>
  <colgroup>
    <col style="width: 30%;">
    <col style="width: 50%;">
    <col style="width: 20%;">
  </colgroup>
  <tr>
    <th>Product</th>
    <th>Description</th>
    <th>Price</th>
  </tr>
  <tr>
    <td>Laptop</td>
    <td>15-inch laptop</td>
    <td>$999</td>
  </tr>
</table>

Стилизация таблиц

border-collapse: collapse объединяет смежные границы в одну. Используйте :nth-child(even) для зебра-полосатых строк (улучшает читаемость). :hover подсвечивает строки при наведении. Стилизация th отличает заголовки. Держите стилизацию таблиц чистой и читаемой — избегайте избыточных границ. Адаптивные таблицы могут требовать overflow-x: auto на обёртке для маленьких экранов.

html
<style>
  table { border-collapse: collapse; width: 100%; }
  th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
  th { background-color: #f4f4f4; }
  tr:nth-child(even) { background-color: #f9f9f9; }
  tr:hover { background-color: #e0e0e0; }
</style>
<table>
  <tr><th>Name</th><th>Score</th></tr>
  <tr><td>Alice</td><td>95</td></tr>
  <tr><td>Bob</td><td>87</td></tr>
</table>
07

Формы и ввод

Структура формы

<form> обёртывает элементы ввода. action указывает URL отправки, method — GET (виден в URL) или POST (скрытый). enctype='multipart/form-data' требуется для загрузки файлов. <fieldset> группирует связанные поля, <legend> предоставляет подпись. Всегда связывайте <label> с input через for/id для доступности.

html
<form action="/submit" method="POST" enctype="multipart/form-data">
  <fieldset>
    <legend>Personal Information</legend>

    <label for="name">Name:</label>
    <input type="text" id="name" name="name" required>

    <label for="email">Email:</label>
    <input type="email" id="email" name="email" required>
  </fieldset>

  <button type="submit">Submit</button>
</form>

Типы ввода

HTML5 представил множество типов ввода со встроенной валидацией и мобильными клавиатурами. type='email' проверяет формат email. type='number' предоставляет спиннеры. type='date' и 'time' показывают нативные пикеры. type='color' открывает палитру. type='range' создаёт слайдер. type='file' с accept ограничивает типы файлов. type='hidden' хранит данные, не показываемые пользователям.

html
<input type="text" name="username" placeholder="Username">
<input type="email" name="email" required>
<input type="password" name="pwd" minlength="8">
<input type="number" name="age" min="0" max="120" step="1">
<input type="date" name="birthday">
<input type="time" name="appointment">
<input type="color" name="favcolor" value="#ff0000">
<input type="range" name="volume" min="0" max="100">
<input type="file" name="upload" accept="image/*">
<input type="hidden" name="token" value="abc123">

Select и Textarea

<select> создаёт выпадающий список. <optgroup> группирует связанные опции с меткой. <option> определяет варианты; атрибут selected предварительно выбирает один. <textarea> — для многострочного текстового ввода. rows и cols устанавливают видимый размер. maxlength ограничивает количество символов. placeholder предоставляет подсказку. Оба поддерживают атрибуты required и disabled.

html
<label for="country">Country:</label>
<select id="country" name="country">
  <optgroup label="North America">
    <option value="us">United States</option>
    <option value="ca">Canada</option>
  </optgroup>
  <optgroup label="Europe">
    <option value="uk">United Kingdom</option>
    <option value="fr">France</option>
  </optgroup>
</select>

<label for="bio">Bio:</label>
<textarea id="bio" name="bio" rows="4" cols="40"
  maxlength="500" placeholder="Tell us about yourself"></textarea>

Чекбоксы и радиокнопки

Чекбоксы допускают множественный выбор; радиокнопки — только один (когда имеют одинаковое name). Атрибут checked предварительно выбирает опцию. Всегда оборачивайте input в <label> для кликабельного текста. Используйте <fieldset> и <legend> для группировки связанных вариантов для доступности. Значение отправляется на сервер, только если выбрано.

html
<fieldset>
  <legend>Select your interests:</legend>
  <label><input type="checkbox" name="interest" value="tech" checked> Tech</label>
  <label><input type="checkbox" name="interest" value="music"> Music</label>
  <label><input type="checkbox" name="interest" value="sports"> Sports</label>
</fieldset>

<fieldset>
  <legend>Choose a plan:</legend>
  <label><input type="radio" name="plan" value="free" checked> Free</label>
  <label><input type="radio" name="plan" value="pro"> Pro</label>
  <label><input type="radio" name="plan" value="enterprise"> Enterprise</label>
</fieldset>

Валидация форм

HTML5 предоставляет встроенную клиентскую валидацию. required предотвращает отправку, если пусто. minlength/maxlength ограничивают длину текста. pattern использует regex для пользовательской валидации (атрибут title направляет пользователей). type='email' и type='url' проверяют формат. min/max работают с числами и датами. Добавьте novalidate к форме для отключения валидации.

html
<form>
  <input type="text" name="username" required
         minlength="3" maxlength="20"
         pattern="[A-Za-z0-9_]+"
         title="3-20 alphanumeric characters">

  <input type="email" name="email" required>

  <input type="url" name="website"
         placeholder="https://example.com">

  <input type="number" name="age" min="18" max="99">

  <button type="submit">Submit</button>
</form>

Кнопки и Datalist

type='submit' отправляет форму, type='reset' очищает все поля, type='button' — пользовательская кнопка. <datalist> предоставляет предложения автодополнения для элементов <input> — связывайте их через list/id. В отличие от <select>, пользователи могут вводить собственные значения. Это комбинирует гибкость текстового ввода с удобством предложений.

html
<!-- Button types -->
<button type="submit">Submit Form</button>
<button type="reset">Reset</button>
<button type="button" onclick="alert('Hi')">Click Me</button>

<!-- Datalist for autocomplete suggestions -->
<label for="browser">Choose a browser:</label>
<input list="browsers" name="browser" id="browser">
<datalist id="browsers">
  <option value="Chrome">
  <option value="Firefox">
  <option value="Safari">
  <option value="Edge">
</datalist>
08

Семантический HTML

Header и навигация

<header> представляет вводный контент — обычно логотип, заголовок и навигацию. Может появляться вверху страницы или внутри <article> или <section>. <nav> идентифицирует навигационные ссылки, помогая скринридерам переходить к навигации. На странице может быть несколько header'ов (например, по одному на секцию). Header — не то же, что head — это видимый контент.

html
<header>
  <h1>My Website</h1>
  <nav>
    <ul>
      <li><a href="/">Home</a></li>
      <li><a href="/blog">Blog</a></li>
      <li><a href="/contact">Contact</a></li>
    </ul>
  </nav>
</header>

Main и Article

<main> обёртывает основной контент страницы (только один на страницу). <article> представляет самодостаточный контент, который можно распространять независимо (пост блога, новостная статья, пост форума). <time> с datetime предоставляет машиночитаемые даты для SEO. <section> группирует тематически связанный контент с заголовком. Эти теги улучшают структуру документа и доступность.

html
<main>
  <article>
    <h1>Blog Post Title</h1>
    <p>Published on <time datetime="2024-01-15">January 15, 2024</time></p>
    <p>The main content of the article...</p>
    <section>
      <h2>Subsection</h2>
      <p>More content...</p>
    </section>
  </article>
</main>

Section и Aside

<section> группирует связанный контент с заголовком — используйте, когда контент имеет естественный заголовок. <aside> представляет контент, косвенно связанный с основным (боковые панели, врезки, реклама, связанные ссылки). Оба улучшают структуру документа. Section могут быть вложены. Aside обычно стилизуются как боковые панели, но семантические, а не презентационные.

html
<section>
  <h2>Services</h2>
  <p>We offer the following services:</p>
  <article>
    <h3>Web Design</h3>
    <p>Custom website design...</p>
  </article>
</section>

<aside>
  <h3>Related Articles</h3>
  <ul>
    <li><a href="/post1">Related Post 1</a></li>
    <li><a href="/post2">Related Post 2</a></li>
  </ul>
</aside>

Footer

<footer> содержит контент подвала для страницы или секции — копирайт, ссылки на связанные документы, контактную информацию, sitemap. На странице может быть несколько footer'ов (например, по одному на секцию статьи). Footer не обязательно должны быть визуально внизу, но семантически представляют заключительный контент. Используйте &copy; для символа копирайта.

html
<footer>
  <div>
    <h3>About Us</h3>
    <p>Company information...</p>
  </div>
  <nav>
    <a href="/privacy">Privacy Policy</a>
    <a href="/terms">Terms of Service</a>
    <a href="/sitemap">Sitemap</a>
  </nav>
  <p>&copy; 2024 My Company. All rights reserved.</p>
</footer>

Details и Summary

<details> создаёт интерактивный виджет раскрытия — контент скрыт, пока пользователь не нажмёт <summary>. Атрибут open показывает его развёрнутым по умолчанию. Это нативный HTML, JavaScript не нужен. Отлично для FAQ, сворачиваемых секций и прогрессивного раскрытия. Вложенные details создают многоуровневые аккордеоны. Стилизуйте через селектор атрибута [open].

html
<details>
  <summary>Click to expand FAQ</summary>
  <p>This content is hidden by default and shown when the summary is clicked.</p>
</details>

<!-- Open by default -->
<details open>
  <summary>What is HTML?</summary>
  <p>HTML (HyperText Markup Language) is the standard language for creating web pages.</p>
</details>

<!-- Nested details -->
<details>
  <summary>Advanced Topics</summary>
  <details>
    <summary>Sub-topic</summary>
    <p>Nested content...</p>
  </details>
</details>
09

Мета-теги и SEO

Charset и Viewport

charset='UTF-8' объявляет кодировку символов — необходимо для корректного отображения всех символов. Мета-тег viewport критичен для адаптивного дизайна: width=device-width соответствует ширине устройства, initial-scale=1.0 устанавливает уровень масштабирования. Без него мобильные браузеры рендерят страницы десктопной ширины. Эти два мета-тега должны быть в каждом HTML-документе.

html
<head>
  <!-- Character encoding -->
  <meta charset="UTF-8">

  <!-- Responsive viewport -->
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0">

  <!-- Internet Explorer compatibility -->
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
</head>

Description и Keywords

Мета-тег description — самый важный для SEO — поисковые системы отображают его под вашим заголовком в результатах (держите до 160 символов). keywords в основном игнорируется современными поисковыми системами. robots контролирует индексацию: index/noindex, follow/nofollow. author указывает создателя контента. Пишите убедительные описания для улучшения CTR.

html
<meta name="description"
      content="Free HTML tutorials for beginners. Learn HTML tags, attributes, and semantic markup with examples.">

<meta name="keywords"
      content="HTML, tutorial, web development, markup">

<meta name="author" content="John Doe">
<meta name="robots" content="index, follow">

Open Graph (Facebook)

Мета-теги Open Graph (og:) контролируют, как ваша страница выглядит при шеринге в Facebook, LinkedIn и других платформах. og:title, og:description, og:image и og:url — самые важные. og:type может быть article, website, product и т.д. Изображение должно быть минимум 1200x630 пикселей. Тестируйте с Facebook Sharing Debugger перед развёртыванием.

html
<meta property="og:title" content="My Amazing Article">
<meta property="og:description" content="A brief description of the article.">
<meta property="og:image" content="https://example.com/image.jpg">
<meta property="og:url" content="https://example.com/article">
<meta property="og:type" content="article">
<meta property="og:site_name" content="My Website">
<meta property="og:locale" content="en_US">

Twitter Cards

Мета-теги Twitter Card контролируют, как ссылки отображаются в Twitter/X. twitter:card может быть summary, summary_large_image или player. Карточка с большим изображением самая привлекательная. twitter:site и twitter:creator ссылаются на аккаунты Twitter. Если присутствуют теги Open Graph, Twitter использует их как fallback, но явные Twitter-теги дают больше контроля. Тестируйте с Twitter Card Validator.

html
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="My Amazing Article">
<meta name="twitter:description" content="A brief description.">
<meta name="twitter:image" content="https://example.com/image.jpg">
<meta name="twitter:site" content="@mywebsite">
<meta name="twitter:creator" content="@author">

Canonical и структурированные данные

Тег canonical link сообщает поисковым системам предпочтительный URL для страницы, предотвращая штрафы за дублирующий контент. Структурированные данные JSON-LD помогают поисковым системам понимать ваш контент и включают расширенные сниппеты (рейтинги звёзд, хлебные крошки, FAQ-аккордеоны в результатах поиска). Используйте типы schema.org вроде Article, Product, Event или FAQPage. Валидируйте с Google Rich Results Test.

html
<!-- Canonical URL (prevents duplicate content issues) -->
<link rel="canonical" href="https://example.com/article">

<!-- Structured data (JSON-LD) -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "My Article Title",
  "author": "John Doe",
  "datePublished": "2024-01-15",
  "image": "https://example.com/image.jpg"
}
</script>
10

SVG и Canvas

Основы SVG

SVG (Scalable Vector Graphics) создаёт независимую от разрешения графику, которая масштабируется без потери качества. Инлайновый SVG можно стилизовать с CSS и манипулировать с JavaScript. Распространённые фигуры: <circle>, <rect>, <line>, <ellipse>, <polygon>, <path>. SVG идеален для иконок, логотипов и диаграмм. В отличие от растровых изображений, SVG чёткие при любом размере.

html
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
  <circle cx="100" cy="100" r="50" fill="blue" stroke="black" stroke-width="2"/>
  <text x="100" y="105" text-anchor="middle" fill="white">Circle</text>
</svg>

<!-- Inline SVG (can be styled with CSS) -->
<svg width="100" height="100">
  <rect x="10" y="10" width="80" height="80" fill="red" rx="10"/>
</svg>

SVG-фигуры

SVG-фигуры используют атрибуты на основе координат. <rect> требует x, y, width, height; rx/ry создают скруглённые углы. <circle> требует cx, cy (центр) и r (радиус). <ellipse> использует rx и ry для разных радиусов x/y. <line> соединяет x1,y1 с x2,y2. <polygon> принимает список точек. fill устанавливает цвет заливки, stroke — цвет контура.

html
<svg width="300" height="200">
  <!-- Rectangle -->
  <rect x="10" y="10" width="80" height="50" fill="orange" rx="10" ry="10"/>

  <!-- Circle -->
  <circle cx="200" cy="50" r="40" fill="green"/>

  <!-- Ellipse -->
  <ellipse cx="100" cy="150" rx="60" ry="30" fill="purple"/>

  <!-- Line -->
  <line x1="10" y1="180" x2="290" y2="180" stroke="black" stroke-width="2"/>

  <!-- Polygon -->
  <polygon points="250,10 290,80 210,80" fill="pink"/>
</svg>

SVG-пути

<path> — самый мощный SVG-элемент, использует атрибут d с командами: M (moveto), L (lineto), H/V (горизонтальная/вертикальная линия), C (кубическая Безье), Q (квадратичная Безье), A (дуга), Z (закрыть путь). Заглавные = абсолютные координаты, строчные = относительные. viewBox определяет систему координат. SVG-иконки используют path для масштабируемых, стилизуемых иконок.

html
<svg width="200" height="200">
  <!-- M=move, L=line, C=curve, Z=close -->
  <path d="M 10 10 L 100 10 L 100 100 Z" fill="none" stroke="black"/>

  <!-- Bezier curve -->
  <path d="M 10 100 C 50 10, 150 10, 190 100" fill="none" stroke="red"/>

  <!-- Arc -->
  <path d="M 10 100 A 90 90 0 0 1 190 100" fill="none" stroke="blue"/>
</svg>

<!-- SVG icon example -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
  <path d="M12 2L2 7v10l10 5 10-5V7z"/>
</svg>

Основы Canvas

<canvas> — растровая поверхность для рисования, манипулируемая через JavaScript. Получите 2D-контекст через getContext('2d'). В отличие от SVG, canvas на основе пикселей (не масштабируется) и не является частью DOM. Canvas лучше для сложных анимаций, игр и обработки изображений. SVG лучше для статической графики, иконок и интерактивных диаграмм. Выбирайте на основе ваших потребностей.

html
<canvas id="myCanvas" width="300" height="200"></canvas>

<script>
  const canvas = document.getElementById('myCanvas');
  const ctx = canvas.getContext('2d');

  // Draw a rectangle
  ctx.fillStyle = 'blue';
  ctx.fillRect(10, 10, 100, 80);

  // Draw a circle
  ctx.beginPath();
  ctx.arc(200, 50, 30, 0, Math.PI * 2);
  ctx.fillStyle = 'red';
  ctx.fill();

  // Draw text
  ctx.font = '20px Arial';
  ctx.fillText('Hello Canvas', 50, 150);
</script>

Инлайновые SVG-иконки

SVG <symbol> определяет переиспользуемые иконки, на которые ссылаются через <use href='#id'>. Это техника SVG-спрайтов — определите иконки один раз, используйте где угодно. symbol скрыт (display:none) и инстанцируется через <use>. Иконки наследуют цвет от CSS 'color' через fill='currentColor'. Этот подход эффективен (одно определение, много использований) и полностью стилизуем.

html
<!-- Reusable SVG symbol -->
<svg style="display:none">
  <symbol id="icon-home" viewBox="0 0 24 24">
    <path d="M12 2L2 12h3v8h6v-6h2v6h6v-8h3z"/>
  </symbol>
</svg>

<!-- Use the icon -->
<svg width="24" height="24" fill="currentColor">
  <use href="#icon-home"/>
</svg>

<!-- Styled icon -->
<svg width="32" height="32" class="icon" style="color: blue;">
  <use href="#icon-home"/>
</svg>
11

Доступность (ARIA)

ARIA-роли

ARIA-роли определяют, что элемент делает для скринридеров. Роли ориентиров (banner, navigation, main, complementary, contentinfo) позволяют пользователям скринридеров перемещаться между регионами страницы. Семантические элементы HTML5 (<header>, <nav>, <main>) имеют неявные ARIA-роли, поэтому добавление атрибутов role часто избыточно — но полезно для старых браузеров. Всегда предпочитайте семантический HTML ARIA, когда возможно.

html
<!-- Landmark roles for page structure -->
<header role="banner">Site Header</header>
<nav role="navigation">Main Menu</nav>
<main role="main">Primary Content</main>
<aside role="complementary">Sidebar</aside>
<footer role="contentinfo">Footer</footer>
<form role="search">Search Form</form>

<!-- Document structure roles -->
<article role="article">Blog Post</article>
<section role="region" aria-label="Comments">Comments</section>

aria-label и aria-labelledby

aria-label определяет доступное имя, когда нет видимой метки (например, кнопки только с иконкой). aria-labelledby ссылается на ID видимого текстового элемента(ов), который метит элемент управления — полезно, когда видимая метка уже на экране. При перечислении нескольких ID их текст конкатенируется по порядку. Предпочитайте aria-labelledby, когда есть видимый текст, так как он держит метки синхронизированными.

html
<!-- aria-label provides accessible name directly -->
<button aria-label="Close menu" onclick="closeMenu()">✕</button>
<input type="search" aria-label="Search products" />

<!-- aria-labelledby references visible text -->
<div id="billing-label">Billing Address</div>
<input type="text" aria-labelledby="billing-label" />

<!-- Multiple labels combined -->
<span id="city">City</span>
<span id="required">required</span>
<input aria-labelledby="city required" />

aria-describedby и подсказки

aria-describedby связывает элемент с дополнительным описательным текстом через ID элемента. Скринридеры объявляют это описание после метки. Идеально для текста справки, подсказок и сообщений об ошибках. Используйте role='alert' для сообщений об ошибках, чтобы они объявлялись немедленно при появлении. Устанавливайте aria-invalid='true' на полях с ошибками, чтобы скринридеры указывали некорректное состояние.

html
<label for="password">Password</label>
<input type="password" id="password"
       aria-describedby="pwd-help pwd-rules" />
<p id="pwd-help">Must be at least 8 characters</p>
<p id="pwd-rules">Include uppercase, number, and symbol</p>

<!-- Error messaging -->
<input type="email" id="email" aria-describedby="email-error" aria-invalid="true" />
<p id="email-error" role="alert">Please enter a valid email address</p>

Живые регионы (aria-live)

Живые регионы объявляют изменения динамического контента пользователям скринридеров. aria-live='polite' ждёт паузы перед объявлением (хорошо для обновлений статуса). aria-live='assertive' прерывает немедленно (для критических ошибок). aria-atomic='true' читает всё содержимое региона вместо только изменённой части. aria-relevant указывает, какие типы изменений (добавления, удаления, текст) вызывают объявления. Существенно для SPA, чат-приложений и автообновляемого контента.

html
<!-- Polite: announces when user is idle -->
<div aria-live="polite" id="status">Saving...</div>

<!-- Assertive: announces immediately, interrupts -->
<div aria-live="assertive" role="alert" id="errors">
  Connection lost!
</div>

<!-- aria-atomic reads entire region, not just changes -->
<div aria-live="polite" aria-atomic="true" id="cart">
  3 items in cart
</div>

<!-- aria-relevant controls what changes are announced -->
<div aria-live="polite" aria-relevant="additions text">
  Chat messages appear here
</div>

Управление фокусом и tabindex

tabindex контролирует поведение фокуса клавиатуры. tabindex='0' делает неинтерактивные элементы (div, span) фокусируемыми в естественном порядке DOM — используйте для пользовательских виджетов. tabindex='-1' делает элементы фокусируемыми только программно через JS .focus() — полезно для модальных окон и целей пропуска. Никогда не используйте положительные значения tabindex, так как они переопределяют естественный порядок табуляции и создают путаницу. Всегда предоставляйте skip-ссылки как первый фокусируемый элемент на странице.

html
<!-- tabindex="0": element is focusable in natural order -->
<div tabindex="0" role="button" onkeypress="handleKey()">
  Custom clickable div
</div>

<!-- tabindex="-1": focusable only via JS, removed from tab order -->
<div tabindex="-1" id="modal">Modal content</div>
<script>
  document.getElementById('modal').focus();
</script>

<!-- tabindex="1+": DO NOT USE — breaks natural tab order -->
<!-- Avoid: <div tabindex="2"> -->

<!-- Skip link for keyboard users -->
<a href="#main-content" class="skip-link">Skip to main content</a>
<main id="main-content">...</main>

Скрытый контент и только для скринридеров

aria-hidden='true' удаляет элементы из дерева доступности, сохраняя их видимыми — используйте для декоративных иконок, избыточного текста или контента вне экрана. Шаблон .sr-only скрывает текст визуально, но сохраняет читаемым для скринридеров — существенно для кнопок только с иконкой. Никогда не используйте aria-hidden на фокусируемых элементах, так как это создаёт разрыв между навигацией с клавиатуры и скринридером. Техника clip — самый надёжный метод визуального скрытия.

html
<!-- aria-hidden: visible but hidden from screen readers -->
<div aria-hidden="true">
  <i class="icon-decoration"></i> Decorative only
</div>

<!-- visually hidden but available to screen readers -->
<style>
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0;
}
</style>
<button>
  <span class="sr-only">Delete item</span>
  <i class="icon-trash" aria-hidden="true"></i>
</button>
12

HTML5 API (Geolocation, Storage, Drag & Drop)

Geolocation API

Geolocation API позволяет запрашивать физическое местоположение пользователя. Браузеры всегда запрашивают разрешение — пользователь должен явно предоставить доступ. getCurrentPosition принимает callback'и успеха и ошибки плюс объект options (enableHighAccuracy, timeout, maximumAge). Для непрерывного отслеживания используйте watchPosition(), который возвращает ID, который можно передать в clearWatch(). Всегда обрабатывайте ошибки корректно (отказ в разрешении, недоступность позиции, тайм-аут). Geolocation требует HTTPS в современных браузерах.

html
<!-- Request user location -->
<button onclick="getLocation()">Get My Location</button>
<p id="demo"></p>

<script>
function getLocation() {
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(
      (pos) => {
        document.getElementById("demo").innerHTML =
          "Lat: " + pos.coords.latitude +
          "<br>Lon: " + pos.coords.longitude;
      },
      (err) => {
        alert("Error: " + err.message);
      },
      { enableHighAccuracy: true, timeout: 5000 }
    );
  } else {
    alert("Geolocation not supported.");
  }
}
</script>

Web Storage (localStorage и sessionStorage)

Web Storage предоставляет key-value хранилище на клиенте. localStorage сохраняется бессрочно; sessionStorage очищается при закрытии вкладки. Оба хранят только строки — используйте JSON.stringify/parse для объектов. Лимит хранения ~5-10 МБ на origin. В отличие от cookie, данные хранения не отправляются с каждым HTTP-запросом. Учитывайте: хранилище синхронное и блокирует основной поток, а также доступно любому скрипту на том же origin (не подходит для чувствительных данных).

html
<!-- localStorage: persists until cleared -->
<script>
// Store data
localStorage.setItem("username", "alice");
localStorage.setItem("prefs", JSON.stringify({theme: "dark", lang: "en"}));

// Retrieve data
const user = localStorage.getItem("username");
const prefs = JSON.parse(localStorage.getItem("prefs") || "{}");

// Remove single item
localStorage.removeItem("username");

// Clear all items
localStorage.clear();

// sessionStorage: cleared when tab closes
sessionStorage.setItem("tempToken", "abc123");
</script>

Drag and Drop API

HTML5 Drag and Drop API обеспечивает нативные drag-взаимодействия. Установите draggable='true' на исходном элементе. ondragstart срабатывает при начале перетаскивания — используйте dataTransfer.setData() для хранения перетаскиваемых данных. ondragover на цели drop ДОЛЖЕН вызывать preventDefault() для разрешения drop. ondrop обрабатывает фактическое drop — извлеките данные через dataTransfer.getData(). Можно перетаскивать файлы из ОС в браузер через event.dataTransfer.files. Для сложных приложений рассмотрите библиотеки вроде SortableJS для лучшей кросс-браузерной поддержки.

html
<div id="drag1" draggable="true" ondragstart="drag(event)">
  Drag me!
</div>
<div id="dropzone" ondrop="drop(event)" ondragover="allowDrop(event)">
  Drop here
</div>

<script>
function allowDrop(ev) {
  ev.preventDefault(); // necessary to allow dropping
}

function drag(ev) {
  ev.dataTransfer.setData("text", ev.target.id);
}

function drop(ev) {
  ev.preventDefault();
  const data = ev.dataTransfer.getData("text");
  ev.target.appendChild(document.getElementById(data));
}
</script>

Page Visibility API

Page Visibility API сообщает, видна ли страница пользователю (не свёрнута, не в фоновой вкладке). Используйте для приостановки дорогих анимаций, воспроизведения видео или опроса, когда пользователь не смотрит — экономит батарею и CPU. document.hidden — boolean; document.visibilityState возвращает 'visible', 'hidden' или 'prerender'. Событие visibilitychange срабатывает при переходах. Это надёжнее, чем blur/focus для обнаружения фактической видимости.

html
<script>
document.addEventListener("visibilitychange", () => {
  if (document.hidden) {
    console.log("Tab is hidden — pause video/animation");
    video.pause();
  } else {
    console.log("Tab is visible — resume");
    video.play();
  }
});

// Check current state
if (document.visibilityState === "visible") {
  console.log("Page is visible");
}
</script>

Fullscreen API

Fullscreen API позволяет отображать любой элемент в полноэкранном режиме. requestFullscreen() должен запускаться пользовательским жестом (клик/нажатие клавиши). Разные браузеры могут требовать вендорные префиксы (webkit, moz, ms). Событие fullscreenchange срабатывает при входе или выходе из полноэкранного режима. document.fullscreenElement ссылается на текущий полноэкранный элемент (null, если не в полноэкранном режиме). Используйте CSS-псевдокласс :fullscreen для другой стилизации полноэкранных элементов.

html
<button onclick="openFullscreen()">Fullscreen</button>
<button onclick="closeFullscreen()">Exit</button>
<div id="container">Content to fullscreen</div>

<script>
function openFullscreen() {
  const elem = document.getElementById("container");
  if (elem.requestFullscreen) {
    elem.requestFullscreen();
  } else if (elem.webkitRequestFullscreen) {
    elem.webkitRequestFullscreen();
  }
}

function closeFullscreen() {
  if (document.exitFullscreen) {
    document.exitFullscreen();
  }
}

document.addEventListener("fullscreenchange", () => {
  console.log("Fullscreen:", !!document.fullscreenElement);
});
</script>
13

Web Components

Custom Elements

Custom Elements позволяют создавать переиспользуемые HTML-теги с инкапсулированным поведением. Класс расширяет HTMLElement (или подклассы HTMLElement). connectedCallback срабатывает при добавлении элемента в DOM — используйте для рендеринга. attributeChangedCallback реагирует на изменения атрибутов, но только для атрибутов, перечисленных в observedAttributes. Имена должны содержать дефис (например, 'my-greeting') для избежания конфликтов с нативным HTML. customElements.define() регистрирует элемент. Избегайте Shadow DOM, если не нужна инкапсуляция стилей.

html
<my-greeting name="World"></my-greeting>

<script>
class MyGreeting extends HTMLElement {
  constructor() {
    super();
    this.name = this.getAttribute("name") || "Guest";
  }
  connectedCallback() {
    this.innerHTML = `<h2>Hello, ${this.name}!</h2>`;
  }
  // Observe attribute changes
  static get observedAttributes() {
    return ["name"];
  }
  attributeChangedCallback(name, oldVal, newVal) {
    if (name === "name") {
      this.name = newVal;
      this.connectedCallback();
    }
  }
}
customElements.define("my-greeting", MyGreeting);
</script>

Shadow DOM

Shadow DOM обеспечивает инкапсуляцию стилей и DOM — стили внутри shadow tree не утекают наружу, а стили страницы не проникают внутрь. attachShadow({mode:'open'}) создаёт shadow root, доступный через element.shadowRoot. mode:'closed' запрещает внешний доступ (редко используется). Элементы <slot> — заполнители, куда проецируются дочерние элементы light DOM. Именованные slot'ы (<slot name='title'>) сопоставляются с дочерними элементами с совпадающими атрибутами slot. ::slotted() стилизует спроецированный контент. Shadow DOM — ключ к созданию самодостаточных, переиспользуемых компонентов.

html
<my-card>
  <span slot="title">Card Title</span>
  <p slot="body">Card content here.</p>
</my-card>

<script>
class MyCard extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: "open" });
    shadow.innerHTML = `
      <style>
        .card { border: 1px solid #ccc; padding: 16px; border-radius: 8px; }
        ::slotted([slot="title"]) { font-size: 1.5em; font-weight: bold; }
      </style>
      <div class="card">
        <slot name="title"></slot>
        <slot name="body"></slot>
      </div>
    `;
  }
}
customElements.define("my-card", MyCard);
</script>

HTML-шаблоны

Элемент <template> хранит HTML, который не рендерится при загрузке страницы — его контент инертен (скрипты не запускаются, изображения не загружаются, стили не применяются). Доступ к контенту через .content (DocumentFragment) и клонируйте через cloneNode(true) для инстанцирования. Шаблоны идеальны для повторяющихся структур, генерируемых JavaScript. В отличие от innerHTML, шаблоны парсятся один раз и могут клонироваться многократно без повторного парсинга. В комбинации с Custom Elements и Shadow DOM они формируют стандарт Web Components.

html
<template id="row-template">
  <tr>
    <td class="name"></td>
    <td class="email"></td>
  </tr>
</template>

<script>
function addRow(name, email) {
  const template = document.getElementById("row-template");
  const clone = template.content.cloneNode(true);
  clone.querySelector(".name").textContent = name;
  clone.querySelector(".email").textContent = email;
  document.querySelector("tbody").appendChild(clone);
}

addRow("Alice", "[email protected]");
addRow("Bob", "[email protected]");
</script>

Callback'и жизненного цикла

Custom Elements имеют четыре callback'а жизненного цикла. constructor() выполняется при создании элемента (избегайте тяжёлой работы здесь). connectedCallback() срабатывает при добавлении в DOM — идеально для настройки и рендеринга. disconnectedCallback() срабатывает при удалении — используйте для очистки (слушатели событий, таймеры). adoptedCallback() срабатывает при перемещении в новый документ через adoptNode() (редко). attributeChangedCallback() срабатывает только для наблюдаемых атрибутов. Порядок upgrade всегда: constructor → connectedCallback. Изменения атрибутов могут срабатывать до connectedCallback.

html
<script>
class LifecycleDemo extends HTMLElement {
  constructor() {
    super();
    console.log("1. constructor: element created");
  }
  connectedCallback() {
    console.log("2. connected: added to DOM");
  }
  disconnectedCallback() {
    console.log("3. disconnected: removed from DOM");
  }
  adoptedCallback() {
    console.log("4. adopted: moved to new document");
  }
  attributeChangedCallback(attr, oldVal, newVal) {
    console.log(`5. attribute '${attr}' changed: ${oldVal} -> ${newVal}`);
  }
  static get observedAttributes() {
    return ["data-status"];
  }
}
customElements.define("lifecycle-demo", LifecycleDemo);
</script>

Customized Built-in Elements

Customized built-in elements расширяют нативные HTML-элементы (вроде button, input, div) для наследования их встроенного поведения. Используйте атрибут 'is' вместо пользовательского имени тега. Третий аргумент customElements.define() указывает, какой нативный элемент расширяется. Это полезно, когда вы хотите улучшить существующие элементы (например, добавить валидацию в <input>), а не создавать полностью новые. Примечание: Safari не поддерживает customized built-in elements — рассмотрите autonomous custom elements для кросс-браузерной совместимости.

html
<!-- Extend a native button -->
<button is="my-button">Click me</button>

<script>
class MyButton extends HTMLButtonElement {
  constructor() {
    super();
    this.addEventListener("click", () => {
      this.textContent = "Clicked!";
      this.style.background = "lightgreen";
    });
  }
  connectedCallback() {
    this.style.fontWeight = "bold";
  }
}
customElements.define("my-button", MyButton,
  { extends: "button" });
</script>
14

Медиа-элементы (Audio и Video)

Элемент Video

Элемент <video> встраивает видео без плагинов. Атрибут controls показывает встроенные элементы управления play/pause/volume. poster указывает предварительное изображение перед воспроизведением. Несколько тегов <source> предоставляют форматные fallback'и — браузер использует первый поддерживаемый (MP4/H.264 наиболее широко поддерживается, WebM открыт и эффективен). Элементы <track> добавляют субтитры, подписи или описания через файлы WebVTT. Всегда включайте fallback-текст для очень старых браузеров. Для autoplay добавьте атрибут muted — большинство браузеров блокируют autoplay со звуком.

html
<video width="640" height="360" controls poster="preview.jpg">
  <source src="movie.mp4" type="video/mp4">
  <source src="movie.webm" type="video/webm">
  <track kind="subtitles" src="subs_en.vtt"
         srclang="en" label="English" default>
  <track kind="captions" src="caps_en.vtt"
         srclang="en" label="English Captions">
  Your browser does not support the video tag.
</video>

Элемент Audio

Элемент <audio> встраивает звук. controls показывает встроенный UI плеера. Без controls элемент невидим — управляйте воспроизведением через JavaScript (play(), pause(), volume, currentTime). preload='auto' предлагает браузеру буферизовать файл; 'metadata' загружает только длительность/инфо; 'none' не загружает ничего до play. MP3 имеет универсальную поддержку; OGG Vorbis открыт, но не поддерживается в Safari. Для игр или точного тайминга используйте Web Audio API вместо <audio> для меньшей задержки и эффектов.

html
<!-- Basic audio player -->
<audio controls>
  <source src="song.mp3" type="audio/mpeg">
  <source src="song.ogg" type="audio/ogg">
  Your browser does not support audio.
</audio>

<!-- Audio controlled by JavaScript -->
<audio id="player" src="song.mp3" preload="auto"></audio>
<button onclick="document.getElementById('player').play()">Play</button>
<button onclick="document.getElementById('player').pause()">Pause</button>
<button onclick="document.getElementById('player').volume += 0.1">Vol+</button>

Picture и srcset (адаптивные изображения)

Адаптивные изображения улучшают производительность, обслуживая изображения соответствующего размера. srcset с дескрипторами ширины (480w, 800w) плюс подсказки sizes позволяют браузеру выбрать лучшее изображение — это предпочтительно для переключения разрешения. <picture> с media query обеспечивает art direction (разные кадры для разных экранов). <source> с атрибутами type предоставляет fallback'и современных форматов (WebP, AVIF) с JPG/PNG fallback. Всегда включайте обычный <img> как последний дочерний элемент <picture> для fallback и доступности.

html
<!-- srcset: let browser choose resolution -->
<img src="small.jpg"
     srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
     sizes="(max-width: 600px) 480px, 800px"
     alt="Responsive image">

<!-- picture: art direction with different images -->
<picture>
  <source media="(max-width: 600px)" srcset="mobile.jpg">
  <source media="(max-width: 1200px)" srcset="tablet.jpg">
  <img src="desktop.jpg" alt="Art-directed image">
</picture>

<!-- Modern format with fallback -->
<picture>
  <source type="image/webp" srcset="photo.webp">
  <source type="image/avif" srcset="photo.avif">
  <img src="photo.jpg" alt="With format fallback">
</picture>

Встраивание iframe

iframe встраивают другой документ в текущую страницу. Атрибут title необходим для доступности — скринридеры объявляют его. Атрибут sandbox ограничивает возможности iframe для безопасности: пустое значение блокирует всё; добавьте токены (allow-scripts, allow-forms, allow-same-origin) для повторного включения конкретных функций. loading='lazy' откладывает загрузку до приближения к viewport. allow указывает политики функций (camera, microphone, autoplay). Будьте осторожны при встраивании недоверенного контента — изолируйте его в sandbox. Кросс-origin iframe недоступны через JavaScript.

html
<!-- Basic iframe -->
<iframe src="https://example.com"
        width="600" height="400"
        title="Embedded Content">
</iframe>

<!-- Sandboxed iframe for security -->
<iframe src="untrusted.html"
        sandbox="allow-scripts allow-same-origin"
        loading="lazy">
</iframe>

<!-- YouTube embed -->
<iframe src="https://www.youtube.com/embed/VIDEO_ID"
        allow="accelerometer; autoplay; encrypted-media"
        allowfullscreen
        title="YouTube video">
</iframe>

Embed и Object

<embed> и <object> — устаревшие методы встраивания, в основном заменённые <iframe> и <video>. <embed> — самозакрывающийся и простой, но не предлагает fallback-контента. <object> более гибкий — контент внутри тегов служит fallback, если встроенный ресурс не может быть отображён. Используйте <object> для PDF и SVG, где нужен fallback. Для современной веб-разработки предпочитайте <iframe> для внешних страниц, <video>/<audio> для медиа и инлайновый <svg> для векторной графики. <embed> в основном используется для контента плагинов (Flash, теперь устарел).

html
<!-- embed: simple, self-closing -->
<embed src="animation.svg" type="image/svg+xml"
       width="300" height="200">

<!-- object: more flexible with fallback -->
<object data="document.pdf" type="application/pdf"
        width="100%" height="600px">
  <p>Unable to display PDF.
     <a href="document.pdf">Download it instead.</a>
  </p>
</object>

<!-- embed YouTube without iframe -->
<embed src="https://www.youtube.com/v/VIDEO_ID"
       type="application/x-shockwave-flash"
       width="560" height="315">
15

Углублённое изучение типов ввода

Ввод даты и времени

HTML5 типы ввода date/time предоставляют нативные пикеры без JavaScript-библиотек. type='date' даёт календарь; type='time' даёт пикер времени; type='datetime-local' объединяет оба. type='month' и type='week' выбирают месяцы и недели. min/max ограничивают выбираемый диапазон. step определяет гранулярность (например, step='1800' для 30-минутных интервалов в секундах). Отображаемый формат зависит от браузера/локали, но отправляемое значение всегда в формате ISO 8601 (YYYY-MM-DD). Не все браузеры одинаково поддерживают все типы — тестируйте кросс-браузерно.

html
<label for="birthday">Birthday:</label>
<input type="date" id="birthday" min="1900-01-01" max="2025-12-31">

<label for="appt">Appointment:</label>
<input type="time" id="appt" min="09:00" max="17:00" step="1800">

<label for="meeting">Meeting:</label>
<input type="datetime-local" id="meeting">

<label for="month">Pick a month:</label>
<input type="month" id="month">

<label for="week">Pick a week:</label>
<input type="week" id="week">

Ввод чисел и диапазона

type='number' предоставляет контроль-спиннер с стрелками вверх/вниз. min, max и step ограничивают значения. Для валюты используйте step='0.01'. type='range' рендерит слайдер — всегда комбинируйте с <output> или видимой меткой, показывающей текущее значение, так как значение не отображается по умолчанию. Number-инпуты фильтруют нечисловой ввод, но всё ещё допускают некоторые недопустимые символы; валидируйте на стороне сервера. Для количеств, не требующих спиннера, рассмотрите type='text' с inputmode='numeric' и pattern-валидацией.

html
<label for="qty">Quantity (1-100):</label>
<input type="number" id="qty" min="1" max="100"
       step="1" value="10">

<label for="price">Price ($):</label>
<input type="number" id="price" min="0" step="0.01"
       placeholder="0.00">

<label for="volume">Volume:</label>
<input type="range" id="volume" min="0" max="100"
       value="50" oninput="out.value=this.value">
<output id="out">50</output>

Ввод цвета и файлов

type='color' открывает нативный пикер цвета и возвращает hex-значение (#rrggbb). type='file' позволяет пользователям выбирать файлы. Атрибут accept фильтрует по MIME-типу или расширению (image/*, .pdf, image/png). Атрибут multiple позволяет выбирать несколько файлов. Доступ к выбранным файлам через свойство files (FileList) в JavaScript. Используйте URL.createObjectURL() для создания локального URL предпросмотра для изображений. Для загрузки больших файлов рассмотрите чанковые загрузки или File API для отслеживания прогресса. Всегда валидируйте типы и размеры файлов на стороне сервера.

html
<label for="color">Pick a color:</label>
<input type="color" id="color" value="#ff0000">

<label for="avatar">Upload avatar:</label>
<input type="file" id="avatar" accept="image/*">

<label for="docs">Upload documents:</label>
<input type="file" id="docs" accept=".pdf,.doc,.docx" multiple>

<!-- File input with image preview -->
<input type="file" accept="image/*" onchange="preview(this)">
<img id="preview-img" hidden>
<script>
function preview(input) {
  const file = input.files[0];
  if (file) {
    document.getElementById("preview-img").src =
      URL.createObjectURL(file);
    document.getElementById("preview-img").hidden = false;
  }
}
</script>

datalist (предложения для ввода)

<datalist> предоставляет предложения автодополнения для полей ввода. Свяжите с input через атрибут list, соответствующий id datalist. В отличие от <select>, пользователи могут вводить любое значение — предложения опциональны. Работает с text, number, date, color и range инпутами. Браузер показывает совпадающие предложения при вводе. Это лёгкая альтернатива JavaScript-библиотекам автодополнения для простых случаев. Примечание: стилизация option внутри datalist не поддерживается — они рендерятся со стандартными стилями браузера.

html
<label for="browser">Favorite browser:</label>
<input list="browsers" id="browser" name="browser">
<datalist id="browsers">
  <option value="Chrome">
  <option value="Firefox">
  <option value="Safari">
  <option value="Edge">
  <option value="Opera">
</datalist>

<!-- Works with various input types -->
<label for="color">Choose color:</label>
<input type="color" list="preset-colors" id="color">
<datalist id="preset-colors">
  <option value="#ff0000">
  <option value="#00ff00">
  <option value="#0000ff">
</datalist>

Атрибуты валидации форм

HTML5 предоставляет встроенную клиентскую валидацию через атрибуты. required предотвращает отправку, если пусто. type='email'/'url' проверяют формат автоматически. minlength/maxlength ограничивают длину текста. min/max ограничивают числа и даты. pattern использует regex для пользовательской валидации. Браузер показывает стандартные пузыри ошибок. Кастомизируйте сообщения через setCustomValidity() — но всегда очищайте при вводе (setCustomValidity('')) для избежания прилипших ошибок. Клиентская валидация улучшает UX, но должна сочетаться с серверной валидацией для безопасности, так как может быть обойдена.

html
<form>
  <label>Email: <input type="email" required></label>
  <label>Username:
    <input type="text" required minlength="3" maxlength="20"
           pattern="[a-zA-Z0-9_]+">
  </label>
  <label>Age:
    <input type="number" min="18" max="120" required>
  </label>
  <label>Website:
    <input type="url" placeholder="https://example.com">
  </label>
  <button type="submit">Submit</button>
</form>

<!-- Custom validation message -->
<input type="text" required
  oninvalid="this.setCustomValidity('Please enter your name')"
  oninput="this.setCustomValidity('')">
16

Service Worker и PWA

Регистрация Service Worker

Service worker — JavaScript-файл, который выполняется в фоновом режиме, отдельно от веб-страницы, обеспечивая офлайн-поддержку, push-уведомления и фоновую синхронизацию. Регистрируйте его с главной страницы — он должен обслуживаться через HTTPS. Расположение файла service worker определяет его scope (он контролирует страницы в своей директории и поддиректориях). Регистрация асинхронная и происходит только один раз на scope. После первого посещения SW активируется при последующих загрузках страницы. Используйте событие load для задержки регистрации до интерактивности страницы.

html
<!-- Register a service worker in main page -->
<script>
if ("serviceWorker" in navigator) {
  window.addEventListener("load", () => {
    navigator.serviceWorker
      .register("/sw.js")
      .then((reg) => {
        console.log("SW registered:", reg.scope);
      })
      .catch((err) => {
        console.log("SW registration failed:", err);
      });
  });
}
</script>

Service Worker: кэширование

Жизненный цикл service worker имеет три фазы: install (кэширование основных ресурсов), activate (очистка старых кэшей) и fetch (перехват сетевых запросов). Стратегия cache-first, показанная здесь, немедленно отдаёт кэшированные ответы, с fallback к сети. Другие стратегии включают network-first (сначала сеть, fallback к кэшу) и stale-while-revalidate (отдаёт кэш, обновляет в фоне). Увеличивайте CACHE_NAME для запуска обновлений кэша. Cache API хранит пары Request/Response. Service worker'ы запускаются только при необходимости и могут быть завершены браузером для экономии памяти.

html
// sw.js - Cache assets for offline use
const CACHE_NAME = "my-app-v1";
const ASSETS = ["/", "/index.html", "/style.css", "/app.js"];

// Install: cache core assets
self.addEventListener("install", (e) => {
  e.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS))
  );
});

// Fetch: serve from cache, fall back to network
self.addEventListener("fetch", (e) => {
  e.respondWith(
    caches.match(e.request).then((cached) => {
      return cached || fetch(e.request);
    })
  );
});

// Activate: clean old caches
self.addEventListener("activate", (e) => {
  e.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(
        keys.filter((k) => k !== CACHE_NAME)
            .map((k) => caches.delete(k))
      )
    )
  );
});

Web App Manifest

Web App Manifest — JSON-файл, который делает веб-приложение устанавливаемым (Add to Home Screen). name — полное имя; short_name появляется на иконке домашнего экрана. display:'standalone' скрывает UI браузера, делая его похожим на нативное приложение. theme_color влияет на цвет хрома браузера. icons должны включать минимум размеры 192px и 512px. 'purpose':'maskable' позволяет Android адаптировать иконку к разным формам. Валидный manifest плюс зарегистрированный service worker — минимальные требования для PWA. Тестируйте с Lighthouse на соответствие PWA.

html
<!-- Link the manifest in HTML -->
<link rel="manifest" href="manifest.json">

<!-- manifest.json -->
{
  "name": "My PWA App",
  "short_name": "MyApp",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#1976d2",
  "orientation": "portrait",
  "icons": [
    {
      "src": "/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icon-512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable"
    }
  ]
}

Push-уведомления

Push-уведомления позволяют повторно вовлекать пользователей, даже когда вкладка закрыта. Сначала запросите разрешение через Notification.requestPermission(). Затем подпишитесь через PushManager — объект подписки содержит URL endpoint и ключи. Отправьте эту подписку на сервер, который использует её для отправки push-сообщений через Web Push API (с VAPID-ключами для аутентификации). Обработчик события push в service worker отображает уведомление. userVisibleOnly:true означает, что каждый push должен показывать уведомление (без тихих push). Требует HTTPS и service worker.

html
<!-- Request notification permission -->
<button onclick="subscribe()">Enable Notifications</button>

<script>
async function subscribe() {
  const permission = await Notification.requestPermission();
  if (permission !== "granted") return;

  const reg = await navigator.serviceWorker.ready;
  const subscription = await reg.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(VAPID_KEY)
  });
  // Send subscription to your server
  await fetch("/api/subscribe", {
    method: "POST",
    body: JSON.stringify(subscription)
  });
}
</script>

<!-- In sw.js: handle push events -->
self.addEventListener("push", (e) => {
  const data = e.data.json();
  e.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: "/icon-192.png",
      badge: "/badge.png"
    })
  );
});

Фоновая синхронизация

Background Sync позволяет отложить действия до стабильного подключения. При регистрации sync-события браузер запускает его при восстановлении подключения — даже если пользователь закрыл вкладку. Обработчик sync-события (в service worker) выполняет отложенную работу. Если работа вызывает ошибку, браузер автоматически повторяет с экспоненциальной задержкой. e.tag идентифицирует тип sync — только один sync на tag в очереди. Используйте IndexedDB для хранения ожидающих данных. Идеально для мессенджеров, отправки форм и синхронизации данных. Periodic Sync (для регулярных обновлений) доступен в Chrome, но не во всех браузерах.

html
// Register a sync event from the page
navigator.serviceWorker.ready.then((reg) => {
  return reg.sync.register("send-messages");
});

// Handle sync in sw.js
self.addEventListener("sync", (e) => {
  if (e.tag === "send-messages") {
    e.waitUntil(sendPendingMessages());
  }
});

async function sendPendingMessages() {
  const messages = await getMessagesFromIndexedDB();
  for (const msg of messages) {
    try {
      await fetch("/api/messages", {
        method: "POST",
        body: JSON.stringify(msg)
      });
      await deleteMessageFromIndexedDB(msg.id);
    } catch (err) {
      throw err; // triggers retry
    }
  }
}
17

Углублённое изучение мета-тегов (Open Graph и Twitter)

Теги Open Graph (Facebook)

Теги Open Graph (OG) контролируют, как ваша страница выглядит при шеринге в Facebook, LinkedIn и большинстве социальных платформ. og:title — заголовок; og:description — краткое описание; og:image — предварительное изображение (рекомендуется 1200x630px). og:type (website, article, video.movie) влияет на рендеринг. Для статей добавьте article:published_time, article:author и article:section для расширенных предпросмотров. Всегда указывайте размеры og:image для более быстрого рендеринга. Тестируйте с Facebook Sharing Debugger. OG-теги существенны для маркетинга в соцсетях и CTR.

html
<head>
  <meta property="og:title" content="My Awesome Article">
  <meta property="og:description" content="A deep dive into modern web development.">
  <meta property="og:type" content="article">
  <meta property="og:url" content="https://example.com/article">
  <meta property="og:image" content="https://example.com/img/og.jpg">
  <meta property="og:image:width" content="1200">
  <meta property="og:image:height" content="630">
  <meta property="og:site_name" content="My Website">
  <meta property="og:locale" content="en_US">
  <meta property="article:published_time" content="2025-01-15T08:00:00Z">
  <meta property="article:author" content="Jane Doe">
</head>

Теги Twitter Card

Теги Twitter Card контролируют, как ссылки отображаются в X (Twitter). Тип twitter:card может быть 'summary' (маленькое изображение), 'summary_large_image' (большое изображение над текстом), 'player' (видео/аудио) или 'app'. twitter:site — @handle сайта; twitter:creator — @handle автора. Изображение должно быть минимум 300x157px (summary) или 600x314px (большое изображение). twitter:image:alt улучшает доступность. Тестируйте с Twitter Card Validator. Даже если есть OG-теги, добавление Twitter-специфичных тегов даёт больше контроля над превью в Twitter.

html
<head>
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:site" content="@mywebsite">
  <meta name="twitter:creator" content="@janedoe">
  <meta name="twitter:title" content="My Awesome Article">
  <meta name="twitter:description" content="A deep dive into modern web dev.">
  <meta name="twitter:image" content="https://example.com/img/twitter.jpg">
  <meta name="twitter:image:alt" content="Article cover image">
</head>

Viewport и мобильные мета-теги

Мета-тег viewport критичен для адаптивного дизайна — без него мобильные браузеры рендерят страницы десктопной ширины и уменьшают масштаб. width=device-width соответствует ширине устройства; initial-scale=1 устанавливает уровень масштабирования. Избегайте отключения пользовательского зума (user-scalable=no), так как это нарушает правила доступности WCAG. apple-mobile-web-app-capable делает приложение полноэкранным при добавлении на домашний экран iOS. theme-color влияет на цвет адресной строки браузера на мобильных Chrome и Safari. Эти мобильные теги значительно улучшают PWA и мобильный опыт.

html
<head>
  <!-- Responsive viewport -->
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0">

  <!-- Disable zoom (accessibility concern — avoid) -->
  <meta name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

  <!-- iOS Safari: full-screen web app -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style"
        content="black-translucent">
  <meta name="apple-mobile-web-app-title" content="MyApp">

  <!-- Android Chrome theme color -->
  <meta name="theme-color" content="#1976d2">
</head>

HTTP-Equiv и Charset

Мета-теги http-equiv симулируют HTTP-заголовки прямо в HTML. charset должен быть первым элементом в <head> и в первых 1024 байтах для предотвращения XSS-атак на основе кодировки. http-equiv='refresh' перенаправляет после задержки — предпочитайте серверные перенаправления для SEO. Content-Security-Policy через meta ограничена (нет report-uri, нет frame-ancestors) — предпочитайте HTTP-заголовки. Cache-Control через meta ненадёжна — используйте HTTP-заголовки. X-UA-Compatible заставляет IE использовать последний движок рендеринга. Всегда предпочитайте реальные HTTP-заголовки вместо http-equiv, когда возможно.

html
<head>
  <!-- Character encoding (must be first in head) -->
  <meta charset="UTF-8">

  <!-- Refresh/redirect after 5 seconds -->
  <meta http-equiv="refresh" content="5; url=https://example.com">

  <!-- Set content security policy -->
  <meta http-equiv="Content-Security-Policy"
        content="default-src 'self'; script-src 'self'">

  <!-- X-UA-Compatible for IE -->
  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  <!-- Cache control -->
  <meta http-equiv="Cache-Control"
        content="no-cache, no-store, must-revalidate">
</head>

Структурированные данные (JSON-LD)

JSON-LD (JavaScript Object Notation for Linked Data) — рекомендованный Google формат для структурированных данных. Помогает поисковым системам понимать ваш контент, обеспечивая расширенные сниппеты (рейтинги звёзд, даты событий, хлебные крошки, FAQ-аккордеоны) в результатах поиска. Распространённые типы: Article, Product, Recipe, Event, LocalBusiness и FAQPage. Поместите script в <head> с type='application/ld+json'. Валидируйте с Google Rich Results Test. Структурированные данные не гарантируют расширенные сниппеты, но делают вас eligible. Это мощный SEO-инструмент для увеличения CTR из результатов поиска.

html
<head>
  <script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@type": "Article",
    "headline": "My Awesome Article",
    "author": {
      "@type": "Person",
      "name": "Jane Doe"
    },
    "datePublished": "2025-01-15",
    "image": "https://example.com/img/article.jpg",
    "publisher": {
      "@type": "Organization",
      "name": "My Website",
      "logo": {
        "@type": "ImageObject",
        "url": "https://example.com/logo.png"
      }
    }
  }
  </script>
</head>
18

Углублённое изучение семантического HTML

Article vs Section vs Div

Выбор правильного элемента-контейнера важен для доступности и SEO. <article> — для контента, который можно распространять независимо (посты блога, новости, комментарии, карточки товаров). <section> группирует тематически связанный контент — всегда должен иметь заголовок. <div> — последнее средство для макета/стилизации, когда не подходит ни один семантический элемент. Вложенные элементы <article> (вроде комментариев внутри поста) допустимы. Правило: если контент можно синдицировать через RSS, используйте <article>; если это тематическая глава — <section>; если только для макета — <div>.

html
<!-- <article>: self-contained, syndicable content -->
<article>
  <h2>Blog Post Title</h2>
  <p>Content that makes sense on its own...</p>
  <article><h3>Comment 1</h3><p>...</p></article>
</article>

<!-- <section>: thematic grouping with a heading -->
<section>
  <h2>Chapter 1: Introduction</h2>
  <p>Related content...</p>
</section>

<!-- <div>: generic container, no semantic meaning -->
<div class="layout-wrapper">
  <div class="grid-item">Styling hook only</div>
</div>

Figure и Figcaption

<figure> представляет самодостаточный контент, на который ссылается основной текст — изображения, диаграммы, листинги кода или цитаты. <figcaption> предоставляет подпись/заголовок. В отличие от обычного <img>, figure/figcaption семантически связывает визуал с его описанием. Скринридеры объявляют figure с их подписями. Figure можно позиционировать где угодно относительно текста, который на них ссылается. Используйте <figure> для любого контента с подписью, на который ссылаются по номеру (Рисунок 1, Листинг 1). Для чисто декоративных изображений используйте обычный <img> с пустым alt.

html
<figure>
  <img src="chart.png" alt="Sales growth chart showing 40% increase">
  <figcaption>
    Figure 1: Quarterly sales growth from Q1 to Q4 2025.
    Data source: Internal sales database.
  </figcaption>
</figure>

<!-- Figure can contain code blocks too -->
<figure>
  <pre><code>const x = 42;</code></pre>
  <figcaption>Listing 1: Variable declaration example</figcaption>
</figure>

Details и Summary (раскрытие)

<details> и <summary> создают нативные сворачиваемые (аккордеонные) секции без JavaScript. Клик по <summary> переключает контент. Атрибут open делает его развёрнутым по умолчанию. Событие toggle срабатывает при разворачивании/сворачивании. Отлично для FAQ, панелей настроек и прогрессивного раскрытия. Скринридеры объявляют это как виджет раскрытия. Можно стилизовать стандартный треугольный маркер через CSS (summary::-webkit-details-marker или list-style). Для сложных взаимодействий может потребоваться JavaScript, но для простых переключателей это нативное решение идеально.

html
<!-- Native collapsible without JavaScript -->
<details>
  <summary>Click to expand FAQ</summary>
  <p>Here is the hidden answer that appears when expanded.</p>
</details>

<!-- Open by default -->
<details open>
  <summary>Already expanded</summary>
  <p>This content is visible on page load.</p>
</details>

<!-- Nested disclosures -->
<details>
  <summary>Level 1</summary>
  <details>
    <summary>Level 2</summary>
    <p>Deeply nested content</p>
  </details>
</details>

Элементы Time и Mark

<time> обёртывает человекочитаемые даты/времена с машиночитаемым атрибутом datetime в формате ISO 8601. Помогает поисковым системам, календарям и ассистивным технологиям корректно парсить даты. datetime поддерживает даты (YYYY-MM-DD), время (HH:MM), datetime с часовым поясом и длительности (PT2H30M). <mark> представляет текст, выделенный по релевантности, как совпадения поисковых терминов. В отличие от <strong> или <em> (которые указывают важность/ударение), <mark> указывает на контекстную релевантность. Оба элемента улучшают семантическое значение и SEO.

html
<!-- <time>: machine-readable dates/times -->
<p>Published on
  <time datetime="2025-01-15">January 15, 2025</time>
</p>
<p>Event at
  <time datetime="2025-03-20T14:30-05:00">2:30 PM EST</time>
</p>
<p>Duration:
  <time datetime="PT2H30M">2 hours 30 minutes</time>
</p>

<!-- <mark>: highlighted/relevant text -->
<p>Search results: the keyword
  <mark>HTML5</mark> appears in 3 documents.
</p>

Элемент Dialog

Элемент <dialog> предоставляет нативные модальные и немодальные диалоги без библиотек. showModal() открывает как модальное (с backdrop, блокирует взаимодействие со страницей); show() открывает немодально. close() закрывает. form method='dialog' закрывает диалог при отправке, со значением кнопки как returnValue. Псевдоэлемент ::backdrop стилизует модальный overlay. Клавиша ESC автоматически закрывает модальные диалоги. Рендеринг в top layer означает, что диалог появляется над всем остальным контентом независимо от z-index. Событие close срабатывает после закрытия. Теперь хорошо поддерживается и заменяет многие JavaScript-библиотеки модальных окон.

html
<!-- Native modal dialog -->
<dialog id="myDialog">
  <h2>Confirm Action</h2>
  <p>Are you sure you want to proceed?</p>
  <form method="dialog">
    <button value="cancel">Cancel</button>
    <button value="confirm">Confirm</button>
  </form>
</dialog>

<button onclick="document.getElementById('myDialog').showModal()">
  Open Modal
</button>

<script>
const dialog = document.getElementById("myDialog");
dialog.addEventListener("close", () => {
  console.log("Dialog closed with:", dialog.returnValue);
});
// Close on backdrop click
dialog.addEventListener("click", (e) => {
  if (e.target === dialog) dialog.close();
});
</script>
19

Продвинутые формы и ввод

Fieldset и Legend

<fieldset> группирует связанные элементы формы, а <legend> предоставляет подпись для группы. Это критично для доступности — скринридеры объявляют legend перед каждым элементом в группе, предоставляя контекст. Атрибут disabled на fieldset отключает все элементы внутри. Fieldset также улучшает визуальную организацию с границей по умолчанию. Для радиокнопок fieldset/legend — рекомендуемый способ метки группы. Избегайте слишком глубокой вложенности fieldset, так как это может запутать пользователей скринридеров.

html
<form>
  <fieldset>
    <legend>Shipping Address</legend>
    <label>Street: <input type="text" name="street" required></label>
    <label>City: <input type="text" name="city" required></label>
    <label>ZIP: <input type="text" name="zip" pattern="[0-9]{5}"></label>
  </fieldset>

  <fieldset disabled>
    <legend>Billing (same as shipping)</legend>
    <label>Card: <input type="text" name="card"></label>
  </fieldset>

  <fieldset>
    <legend>Subscription Plan</legend>
    <label><input type="radio" name="plan" value="free"> Free</label>
    <label><input type="radio" name="plan" value="pro" checked> Pro</label>
  </fieldset>
</form>

Элементы Output и Progress

<output> отображает результат вычисления — имеет live-связь с элементами ввода (через атрибут for). Семантически более значим, чем span для вычисляемых значений. <progress> представляет завершённость задачи (value/max); без value показывает неопределённый спиннер. <meter> представляет скалярное значение в известном диапазоне (как место на диске или результаты тестов) — атрибуты low, high и optimum определяют пороги, влияющие на цвет (зелёный/жёлтый/красный). Оба, progress и meter, имеют встроенную стилизацию, которая зависит от браузера, но может быть кастомизирована через CSS.

html
<form oninput="result.value = parseInt(a.value) + parseInt(b.value)">
  <input type="number" id="a" value="10"> +
  <input type="number" id="b" value="20"> =
  <output name="result" for="a b">30</output>
</form>

<!-- Progress bar -->
<label>Downloading: <progress id="prog" value="70" max="100">70%</progress></label>

<!-- Meter (gauge within a range) -->
<label>Disk usage: <meter value="0.6" min="0" max="1" low="0.5" high="0.8" optimum="0.2">60%</meter></label>
<label>Score: <meter value="85" min="0" max="100" low="40" high="70" optimum="90">85</meter></label>

Автозаполнение форм

Атрибут autocomplete помогает браузерам заполнять формы, используя сохранённые пользовательские данные. Используйте стандартизированные токены: 'name', 'email', 'tel', 'street-address', 'address-level2' (город), 'postal-code', 'country'. Для кредитных карт: 'cc-name', 'cc-number', 'cc-exp'. autocomplete='off' отключает автозаполнение (хотя браузеры могут игнорировать для нечувствительных полей). autocomplete='one-time-code' запускает автозаполнение SMS-кода на мобильных. Правильные токены autocomplete значительно повышают заполняемость форм и UX. Они также помогают менеджерам паролей правильно идентифицировать поля.

html
<form autocomplete="on">
  <!-- Browser can autofill name -->
  <label>Name: <input type="text" name="name" autocomplete="name"></label>

  <!-- Email autofill -->
  <label>Email: <input type="email" name="email" autocomplete="email"></label>

  <!-- Address autofill tokens -->
  <fieldset>
    <legend>Address</legend>
    <input autocomplete="street-address">
    <input autocomplete="address-level2"> <!-- City -->
    <input autocomplete="postal-code">
    <input autocomplete="country">
  </fieldset>

  <!-- Disable autocomplete for sensitive field -->
  <label>SSN: <input type="text" autocomplete="off"></label>

  <!-- One-time code (SMS) -->
  <label>Code: <input type="text" autocomplete="one-time-code"></label>
</form>

Методы отправки форм

GET добавляет данные формы к URL (видно, в закладки, ограниченная длина) — используйте для поиска и фильтров. POST отправляет данные в теле запроса (не видно, без ограничения длины) — используйте для создания/обновления данных. enctype='multipart/form-data' требуется для загрузки файлов. Пара имя/значение кнопки включается в отправку — несколько кнопок submit могут вызывать разные действия через одно имя с разными значениями. Для AJAX-отправки используйте объект FormData и fetch(). Всегда используйте POST для чувствительных данных, так как данные GET появляются в истории браузера и логах сервера.

html
<!-- GET: data in URL query string -->
<form method="GET" action="/search">
  <input name="q" value="html5">
  <!-- URL: /search?q=html5 -->
</form>

<!-- POST: data in request body -->
<form method="POST" action="/submit" enctype="application/x-www-form-urlencoded">
  <input name="name" value="Alice">
</form>

<!-- File upload: multipart -->
<form method="POST" action="/upload" enctype="multipart/form-data">
  <input type="file" name="document">
</form>

<!-- Form with custom submit button -->
<form method="POST" action="/save">
  <button type="submit" name="action" value="save">Save</button>
  <button type="submit" name="action" value="publish">Publish</button>
</form>

Contenteditable и Spellcheck

contenteditable='true' делает любой элемент напрямую редактируемым в браузере — основа rich-text редакторов. spellcheck='true' включает проверку орфографии браузером (красное подчёркивание для ошибок). Для фрагментов кода установите spellcheck='false' для избежания ложноположительных. Атрибут contenteditable может наследоваться — дочерние элементы редактируемые, если не установлено 'false'. Сохранение редактируемого контента требует JavaScript (например, хранение в localStorage при input). Создание полноценного rich-text редактора с contenteditable сложно (обработка вставки, форматирования, позиции курсора) — рассмотрите библиотеки вроде Quill, TipTap или ProseMirror для продакшена.

html
<!-- Editable div -->
<div contenteditable="true">
  Click to edit this text directly in the browser.
</div>

<!-- Editable with spellcheck -->
<p contenteditable="true" spellcheck="true">
  Typoos will be underlined in red.
</p>

<!-- Turn off spellcheck for code -->
<pre contenteditable="true" spellcheck="false">
  const varible = "code"; // no spellcheck
</pre>

<!-- Entire document editable -->
<body contenteditable="true">

<!-- Save editable content -->
<div id="note" contenteditable="true"
     oninput="localStorage.setItem('note', this.innerHTML)">
  <script>document.getElementById('note').innerHTML =
    localStorage.getItem('note') || '';</script>
</div>
20

SEO мета-теги

Теги Open Graph

Теги Open Graph (созданные Facebook) контролируют, как ваша страница выглядит при шеринге в соцсетях. og:image должен быть 1200x630px. Без этих тегов платформы придумывают превью из контента страницы, часто плохо.

html
<meta property="og:title" content="My Awesome Article">
<meta property="og:description" content="A deep dive into modern web design.">
<meta property="og:image" content="https://example.com/cover.jpg">
<meta property="og:url" content="https://example.com/article">
<meta property="og:type" content="article">
<meta property="og:site_name" content="Example Site">

Twitter Cards

Теги Twitter Card контролируют, как ссылки рендерятся в X/Twitter. summary_large_image показывает заметное изображение над заголовком; summary показывает меньший квадратный эскиз. Тег twitter:site связывает карточку с вашим аккаунтом.

html
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@yoursite">
<meta name="twitter:title" content="My Article">
<meta name="twitter:description" content="A deep dive into web design.">
<meta name="twitter:image" content="https://example.com/cover.jpg">

Canonical и Robots

rel="canonical" предотвращает штрафы за дублирующий контент, указывая на основной URL. Мета-тег robots направляет краулеров: noindex удаляет страницу из результатов, nofollow блокирует переход по ссылкам.

html
<!-- Canonical: tells search engines the preferred URL -->
<link rel="canonical" href="https://example.com/article">

<!-- Robots: control indexing -->
<meta name="robots" content="index, follow">
<meta name="robots" content="noindex, nofollow">
<meta name="robots" content="noindex, follow">

Структурированные данные (JSON-LD)

Структурированные данные JSON-LD помогают поисковым системам понимать контент и обеспечивают расширенные результаты (рейтинги звёзд, хлебные крошки, инфо о событиях). Schema.org определяет словарь. Поместите тег script внутри head или body.

html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "Modern Web Design",
  "author": { "@type": "Person", "name": "Jane Doe" },
  "datePublished": "2025-01-15",
  "image": "https://example.com/cover.jpg"
}
</script>

Meta Description и Viewport

Мета-тег description влияет на CTR — держите до 160 символов. Тег viewport обязателен для адаптивного дизайна на мобильных. theme-color стилизует UI-бар браузера. charset должен быть первым элементом в head.

html
<meta name="description" content="A concise 150-160 character summary that appears in search results.">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#4285f4">
<meta charset="UTF-8">
21

Основы SVG

Базовые фигуры

SVG рисует XML-элементами. Фигуры включают rect, circle, ellipse, line, polyline и polygon. В отличие от растровых изображений, SVG масштабируются без потери качества и могут стилизоваться через CSS. fill устанавливает цвет заливки, stroke — контур.

html
<svg width="200" height="150" xmlns="http://www.w3.org/2000/svg">
  <rect x="10" y="10" width="80" height="50" fill="steelblue"/>
  <circle cx="140" cy="40" r="30" fill="tomato"/>
  <line x1="10" y1="100" x2="180" y2="100" stroke="black" stroke-width="2"/>
  <ellipse cx="100" cy="120" rx="60" ry="20" fill="none" stroke="green"/>
</svg>

Пути

Элемент path — самый мощный SVG-примитив. Команды: M (move), L (line), H/V (горизонтальная/вертикальная), C (кубическая Безье), Q (квадратичная), A (дуга), Z (закрыть). Заглавные = абсолютные координаты, строчные = относительные.

html
<svg width="200" height="200">
  <!-- M=move, L=line, C=cubic bezier, Z=close -->
  <path d="M 10 10 L 100 10 L 100 100 Z" fill="none" stroke="black"/>
  <path d="M 10 150 C 50 50, 150 50, 190 150" stroke="red" fill="none"/>
  <path d="M 10 180 Q 100 120 190 180" stroke="blue" fill="none"/>
</svg>

Группы и переиспользование

Группируйте элементы с g для применения общих атрибутов или трансформации. Определяйте переиспользуемые фигуры внутри defs и ссылайтесь через use href="#id". Это держит SVG DRY и меньше.

html
<svg width="200" height="200">
  <defs>
    <g id="star">
      <polygon points="50,5 61,39 98,39 68,61 79,95 50,75 21,95 32,61 2,39 39,39"/>
    </g>
  </defs>
  <use href="#star" x="0" y="0" fill="gold"/>
  <use href="#star" x="100" y="100" fill="orange"/>
</svg>

Текст и стилизация

SVG-текст — настоящий текст — выделяемый, ищемый и чёткий при любом масштабе. font-family, font-size, font-weight зеркалируют CSS. text-anchor контролирует горизонтальное выравнивание (start/middle/end).

html
<svg width="300" height="100">
  <text x="10" y="50" font-family="Arial" font-size="24" fill="navy">
    Hello SVG
  </text>
  <text x="150" y="80" font-size="16" font-weight="bold" text-anchor="middle">
    Centered Bold Text
  </text>
</svg>

Градиенты и паттерны

Градиенты определяются в defs и ссылаются через fill="url(#id)". linearGradient переходит вдоль линии; radialGradient излучает из центральной точки. Каждый stop определяет цвет на процентном смещении.

html
<svg width="200" height="100">
  <defs>
    <linearGradient id="lg" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="0%" stop-color="red"/>
      <stop offset="100%" stop-color="blue"/>
    </linearGradient>
    <radialGradient id="rg">
      <stop offset="0%" stop-color="yellow"/>
      <stop offset="100%" stop-color="green"/>
    </radialGradient>
  </defs>
  <rect width="100" height="100" fill="url(#lg)"/>
  <rect x="100" width="100" height="100" fill="url(#rg)"/>
</svg>
22

Canvas

Рисование фигур

Canvas API рисует пиксели на canvas-элементе через 2D-контекст рендеринга. fillRect/strokeRect рисуют прямоугольники; пути (beginPath, arc, lineTo) строят сложные фигуры перед fill() или stroke(). Canvas растровый — масштабирование размывает рисунок.

html
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

ctx.fillStyle = 'tomato';
ctx.fillRect(10, 10, 100, 50);

ctx.strokeStyle = 'navy';
ctx.lineWidth = 3;
ctx.strokeRect(130, 10, 80, 80);

ctx.beginPath();
ctx.arc(200, 150, 40, 0, Math.PI * 2);
ctx.fill();

Пути и линии

Пути строят фигуры точка за точкой. moveTo начинает новый под-путь; lineTo добавляет прямые сегменты; closePath соединяет с началом. bezierCurveTo и quadraticCurveTo рисуют кривые с контрольными точками.

html
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(150, 50);
ctx.lineTo(100, 150);
ctx.closePath();
ctx.fillStyle = 'gold';
ctx.fill();
ctx.stroke();

// Bezier curves
ctx.beginPath();
ctx.moveTo(0, 200);
ctx.bezierCurveTo(100, 100, 200, 300, 300, 200);
ctx.stroke();

Рисование изображений

drawImage рисует изображение, видео или другой canvas. Форма с 9 аргументами обрезает исходный прямоугольник и рисует в целевой — полезно для sprite sheet'ов. Ждите onload перед рисованием.

html
const img = new Image();
img.src = 'photo.jpg';
img.onload = () => {
  ctx.drawImage(img, 0, 0);                    // full image
  ctx.drawImage(img, 0, 0, 200, 150);          // scaled
  ctx.drawImage(img, 50, 50, 100, 100, 300, 0, 100, 100); // cropped
};

Цикл анимации

requestAnimationFrame планирует draw() один раз за repaint браузера (~60fps), приостанавливаясь при скрытой вкладке. clearRect очищает canvas каждый кадр для предотвращения следов. Для физики умножайте движение на delta time для согласованной скорости.

html
let x = 0;
function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = 'steelblue';
  ctx.fillRect(x, 50, 40, 40);
  x += 2;
  if (x > canvas.width) x = -40;
  requestAnimationFrame(draw);
}
requestAnimationFrame(draw);

Манипуляция пикселями

getImageData возвращает Uint8ClampedArray сырых RGBA-значений пикселей (0-255 на канал). Прямой доступ к пикселям обеспечивает фильтры и эффекты. putImageData записывает модифицированный буфер обратно. Медленно для больших canvas.

html
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; // RGBA bytes

for (let i = 0; i < data.length; i += 4) {
  const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
  data[i] = data[i + 1] = data[i + 2] = avg; // grayscale
}

ctx.putImageData(imageData, 0, 0);
23

Iframe и встраивание

Основы iframe

iframe встраивает другой документ внутрь текущей страницы. Атрибут title требуется для доступности. loading="lazy" откладывает загрузку до приближения к viewport. Всегда устанавливайте width и height для предотвращения сдвига макета.

html
<iframe
  src="https://example.com/widget"
  width="600"
  height="400"
  title="Example Widget"
  loading="lazy"
  referrerpolicy="no-referrer">
</iframe>

Атрибут Sandbox

Атрибут sandbox ограничивает возможности iframe. Пустое значение блокирует всё. Добавьте токены для повторного включения функций: allow-scripts, allow-forms, allow-same-origin, allow-popups. Никогда не комбинируйте allow-scripts с allow-same-origin для недоверенного контента.

html
<!-- Locks down the iframe completely -->
<iframe src="untrusted.html" sandbox></iframe>

<!-- Selectively re-enable features -->
<iframe
  src="widget.html"
  sandbox="allow-scripts allow-same-origin allow-forms">
</iframe>

Embed и Object

embed и object — legacy-элементы. object поддерживает fallback-контент, показываемый при неудаче ресурса. Современные HTML5-элементы (video, audio, picture) предпочтительны. Используйте iframe для HTML-контента, video для видео.

html
<!-- Embed for plugins/media -->
<embed src="video.mp4" type="video/mp4" width="400" height="300">

<!-- Object with fallback content -->
<object data="report.pdf" type="application/pdf" width="100%" height="600">
  <p>Your browser cannot display PDFs. <a href="report.pdf">Download</a></p>
</object>

<!-- Video with multiple sources -->
<video controls>
  <source src="movie.webm" type="video/webm">
  <source src="movie.mp4" type="video/mp4">
  Your browser does not support video.
</video>

Связь через postMessage

postMessage — единственный способ общаться через границы iframe (разные origin). Вссегда указывайте target origin в третьем аргументе. На принимающей стороне проверяйте e.origin перед доверием сообщению.

html
<!-- Parent page -->
<iframe id="f" src="child.html"></iframe>
<script>
  const frame = document.getElementById('f');
  frame.contentWindow.postMessage({ type: 'greet', text: 'Hi' }, 'https://example.com');

  window.addEventListener('message', (e) => {
    if (e.origin !== 'https://example.com') return;
    console.log('From child:', e.data);
  });
</script>

Адаптивные iframe

Чтобы сделать iframe адаптивными (например, видео 16:9), оберните их в контейнер с padding-bottom, равным процентному соотношению сторон (56.25% для 16:9). Позиционируйте iframe абсолютно для заполнения контейнера.

html
<div style="position:relative; padding-bottom:56.25%; height:0; overflow:hidden;">
  <iframe
    src="https://youtube.com/embed/abc"
    style="position:absolute; top:0; left:0; width:100%; height:100%; border:0;"
    title="Video"
    allowfullscreen>
  </iframe>
</div>
24

Оптимизация производительности

Resource hints

Resource hints говорят браузеру подготовить соединения или загрузить ресурсы до того, как они понадобятся. preconnect разогревает DNS/TCP/TLS. preload загружает критические ресурсы текущей страницы рано. prefetch загружает ресурсы следующей страницы во время простоя.

html
<!-- Preconnect to a third-party origin -->
<link rel="preconnect" href="https://fonts.googleapis.com">

<!-- DNS prefetch for lower-priority origins -->
<link rel="dns-prefetch" href="https://analytics.example.com">

<!-- Preload critical assets -->
<link rel="preload" href="hero.webp" as="image">
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>

<!-- Prefetch next page -->
<link rel="prefetch" href="/next-page.html">

Ленивая загрузка изображений

loading="lazy" откладывает загрузку внеэкранных изображений до прокрутки к ним. Всегда устанавливайте width и height для предотвращения сдвига макета. Элемент picture обслуживает современные форматы (WebP/AVIF) с JPG fallback.

html
<!-- Native lazy loading -->
<img src="photo.jpg" loading="lazy" width="800" height="600" alt="...">

<!-- Picture with responsive sources -->
<picture>
  <source srcset="small.webp" media="(max-width: 600px)" type="image/webp">
  <source srcset="large.webp" type="image/webp">
  <img src="large.jpg" loading="lazy" width="1200" height="800" alt="...">
</picture>

Стратегии загрузки скриптов

Без атрибутов скрипт блокирует парсинг HTML. async загружает параллельно, но выполняется немедленно при готовности — порядок не гарантирован. defer загружает параллельно и выполняется по порядку после парсинга DOM — лучше для основных скриптов.

html
<!-- Normal: blocks HTML parsing -->
<script src="app.js"></script>

<!-- Async: downloads in parallel, runs ASAP -->
<script src="analytics.js" async></script>

<!-- Defer: downloads in parallel, runs after HTML parse -->
<script src="app.js" defer></script>

<!-- Module scripts defer by default -->
<script type="module" src="app.mjs"></script>

Critical CSS

Инлайнинг critical CSS (стили, нужные для контента выше сгиба) устраняет render-blocking CSS-запросы, ускоряя first paint. Остальной CSS загружается асинхронно через preload-трюк. Инструменты вроде Critical автоматически извлекают critical CSS.

html
<!-- Inline above-the-fold CSS in head -->
<head>
  <style>
    body { margin: 0; font-family: sans-serif; }
    .hero { height: 100vh; background: #f0f0f0; }
  </style>
  <!-- Load the rest asynchronously -->
  <link rel="preload" href="full.css" as="style" onload="this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="full.css"></noscript>
</head>

Измерение производительности

Performance API измеряет реальные тайминги пользователей. Ключевые метрики: FCP (First Contentful Paint), LCP (Largest Contentful Paint), CLS (Cumulative Layout Shift), INP (Interaction to Next Paint). Используйте Lighthouse для лабораторных данных.

html
// PerformanceObserver for Core Web Vitals
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(entry.name, entry.startTime);
  }
}).observe({ entryTypes: ['paint', 'largest-contentful-paint'] });

// Navigation timing
const [nav] = performance.getEntriesByType('navigation');
console.log('DOM Content Loaded:', nav.domContentLoadedEventEnd);
console.log('Load:', nav.loadEventEnd);
25

Продвинутые формы

Типы ввода

HTML5 типы ввода обеспечивают встроенную валидацию и UI. email/url проверяют формат. number/date предоставляют пикеры. color/range имеют специализированный UI. accept фильтрует типы файлов. Браузеры обрабатывают валидацию автоматически.

html
<input type="email" required>
<input type="url" placeholder="https://">
<input type="number" min="0" max="100" step="5">
<input type="date" min="2024-01-01">
<input type="color" value="#ff0000">
<input type="range" min="0" max="10">
<input type="file" accept="image/*">

Валидация форм

required предотвращает отправку, если пусто. minlength/maxlength ограничивают длину. pattern использует regex-валидацию. Браузер предотвращает отправку и показывает ошибки. Кастомизируйте через setCustomValidity и событие invalid.

html
<form>
  <input type="text" required minlength="3" maxlength="20" pattern="[A-Za-z]+">
  <input type="email" required>
  <input type="submit" value="Submit">
</form>

Fieldset и Legend

fieldset группирует связанные поля формы. legend предоставляет подпись. disabled на fieldset отключает все поля. Улучшает доступность, группируя связанные инпуты. Скринридеры объявляют legend для каждого поля.

html
<fieldset>
  <legend>Shipping Address</legend>
  <label>Street: <input type="text" name="street"></label>
  <label>City: <input type="text" name="city"></label>
</fieldset>
<fieldset disabled>
  <legend>Billing (disabled)</legend>
  <input type="text" name="billing">
</fieldset>

Datalist

datalist предоставляет предложения автодополнения для input. Пользователи могут выбирать или вводить свободно. В отличие от select, допускает пользовательские значения. Атрибут list связывает input с datalist по id. Полезно для поиска и тегов.

html
<label>Choose browser:
  <input list="browsers" name="browser">
</label>
<datalist id="browsers">
  <option value="Chrome">
  <option value="Firefox">
  <option value="Safari">
</datalist>

Элемент Output

output отображает вычисленные результаты. Атрибут for ссылается на ID инпутов. Обновляется live при изменениях формы. JavaScript не нужен для простых вычислений. Доступен для скринридеров как live-регион.

html
<form oninput="result.value = parseInt(a.value) + parseInt(b.value)">
  <input type="number" id="a" value="0"> +
  <input type="number" id="b" value="0"> =
  <output name="result" for="a b">0</output>
</form>
26

Доступность (a11y)

ARIA-метки

aria-label предоставляет доступные имена для элементов без видимого текста. aria-expanded указывает состояние toggle. aria-controls ссылается на контролируемый элемент. Используйте ARIA только когда семантики HTML недостаточно.

html
<button aria-label="Close menu" onclick="closeMenu()">
  <svg>...</svg>
</button>
<nav aria-label="Main navigation">...</nav>
<button aria-expanded="false" aria-controls="menu">Menu</button>

Роли ориентиров

Роли ориентиров помогают пользователям скринридеров ориентироваться. Большинство семантических элементов имеют неявные роли. Добавляйте явные роли только при необходимости. role="search" на form создаёт ориентир поиска. Избегайте избыточности ролей с семантическими элементами.

html
<header role="banner">...</header>
<nav role="navigation">...</nav>
<main role="main">...</main>
<aside role="complementary">...</aside>
<footer role="contentinfo">...</footer>
<form role="search">...</form>

Управление фокусом

Управляйте фокусом для пользователей клавиатуры. При открытии модальных окон перемещайте фокус внутрь. При закрытии возвращайте фокус на триггер. Используйте tabindex="-1" для программной фокусируемости. Никогда не удаляйте focus outline без замены.

html
<button id="open">Open</button>
<div id="modal" hidden>
  <button id="close">Close</button>
</div>
<script>
document.getElementById('open').onclick = () => {
  modal.hidden = false;
  document.getElementById('close').focus();
};
</script>

Skip-ссылки

Skip-ссылки позволяют пользователям клавиатуры обходить повторяющуюся навигацию. Скрыты визуально до фокуса. href указывает на ID основного контента. Существенно для соответствия доступности (WCAG). Улучшает эффективность навигации.

html
<body>
  <a href="#main" class="skip-link">Skip to main content</a>
  <nav>...long navigation...</nav>
  <main id="main">...</main>
</body>
<style>
.skip-link { position: absolute; left: -9999px; }
.skip-link:focus { left: 0; }
</style>

Alt-текст

Alt-текст описывает изображения для скринридеров. Пустой alt="" помечает декоративные изображения (игнорируются). Описывайте контент и назначение, а не внешний вид. Для сложных изображений предоставьте более длинное описание в другом месте. Никогда не используйте alt для подсказок (используйте title).

html
<!-- Informative image -->
<img src="chart.png" alt="Bar chart showing 30% increase in sales">
<!-- Decorative image -->
<img src="spacer.gif" alt="">
<!-- Complex image -->
<img src="diagram.png" alt="Network topology" longdesc="diagram-desc.html">
27

Распространённые подводные камни

Отсутствие alt-текста

Отсутствие alt-текста ломает доступность. Скринридеры читают имя файла. 'image' или 'photo' бесполезны. Описывайте контент и назначение. Пустой alt="" помечает декоративные изображения. Никогда не пропускайте атрибут alt полностью.

html
<!-- BAD: no alt -->
<img src="photo.jpg">
<!-- BAD: unhelpful alt -->
<img src="photo.jpg" alt="image">
<!-- GOOD: descriptive alt -->
<img src="photo.jpg" alt="Team meeting in conference room">
<!-- Decorative: empty alt -->
<img src="border.png" alt="">

Div-суп

Использование div для всего удаляет семантическое значение. Скринридеры не могут ориентироваться. SEO не может понимать структуру контента. Используйте семантические элементы: header, nav, main, article, section, aside, footer. Резервируйте div для группировки без семантического намерения.

html
<!-- BAD: div for everything -->
<div class="header">...</div>
<div class="nav">...</div>
<div class="article">...</div>
<!-- GOOD: semantic elements -->
<header>...</header>
<nav>...</nav>
<article>...</article>

Инлайновые стили

Инлайновые стили смешивают контент и представление, усложняя поддержку. Они имеют высокую специфичность, переопределяя стили. Не могут кэшироваться или переиспользоваться. Используйте классы и внешние стили. Резервируйте инлайновые стили для динамических значений.

html
<!-- BAD: inline styles -->
<div style="color: red; font-size: 16px;">Text</div>
<!-- GOOD: external CSS -->
<div class="error">Text</div>
<link rel="stylesheet" href="styles.css">

Button vs Link

Кнопки вызывают действия (сохранить, удалить, переключить). Ссылки навигируют по URL. Использование ссылок для действий ломает навигацию с клавиатуры (Space vs Enter) и семантику. Скринридеры объявляют их по-разному. Используйте type="button" для предотвращения отправки формы.

html
<!-- BAD: link for actions -->
<a href="#" onclick="save()">Save</a>
<!-- GOOD: button for actions -->
<button type="button" onclick="save()">Save</button>
<!-- Link for navigation -->
<a href="/about">About</a>

Иерархия заголовков

Заголовки создают структуру документа. Не пропускайте уровни (h1 к h3). Используйте один h1 на страницу (главный заголовок). Пользователи скринридеров ориентируются по заголовкам. Поддерживайте логичную иерархию. Используйте CSS для визуальной стилизации, а не уровни заголовков.

html
<!-- BAD: skip levels -->
<h1>Title</h1>
<h3>Subtitle</h3>  <!-- Skipped h2 -->
<!-- BAD: multiple h1 -->
<h1>Title</h1>
<h1>Another</h1>
<!-- GOOD: hierarchical -->
<h1>Main Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>

Was this helpful?

Learning path

Learn from scratch

Learn this language from the ground up with structured lessons.