Атрибуты data-* позволяют хранить в HTML-разметке собственные данные, не предусмотренные стандартом - любое имя после data- становится валидным атрибутом, который браузер не трогает, а JavaScript и CSS могут прочитать напрямую.
<div class="card" data-user-id="42" data-status="done">...</div>
В этой статье
Атрибуты data-* в HTML: правила именования
Имя атрибута должно начинаться с data-, дальше допускаются латинские буквы, цифры, дефисы, точки, двоеточия и подчёркивания - заглавные буквы в имени недопустимы, браузер приводит их к нижнему регистру. Значение всегда хранится как строка, даже если выглядит как число или true/false. Один элемент может нести сколько угодно таких атрибутов одновременно.
| Атрибут | Особенность | Комментарий |
|---|---|---|
| data-user-id="42" | Простое имя из одного слова через дефис не нужен | допустимо |
| data-date-of-birth="1990-01-01" | Несколько слов разделяются дефисами | допустимо |
| data-Status="done" | Заглавная буква в имени | браузер сам приведёт к data-status |
| data-foo-1="x" | Дефис перед цифрой | допустимо, но в JS доступен только через скобки - см. ниже |
Как получить значение: dataset и getAttribute
Самый удобный способ прочитать data-атрибут из JavaScript - свойство element.dataset. Часть имени после data- переводится в camelCase: дефис плюс буква превращается в заглавную букву без дефиса, то есть data-date-of-birth становится dataset.dateOfBirth. Дефис перед цифрой в это правило не входит и camelCase не образует - data-foo-1 нельзя прочитать как dataset.foo1, только через запись в квадратных скобках: dataset['foo-1']. Тот же результат даёт и универсальный getAttribute, но ему нужно полное дефисное имя, как в HTML.
Пример: dataset vs getAttribute на одном элементе
dataset переводит имя в camelCase автоматически, а дефис перед цифрой camelCase не образует - только dataset['foo-1'].
Поиск элементов по data-атрибуту: CSS и querySelector
data-атрибуты участвуют в обычных CSS-селекторах атрибутов - [data-status="done"] выберет элемент с точным значением, а [data-status^="error"] - любой, чьё значение начинается с error. Это же работает и в querySelector/querySelectorAll из JavaScript, причём там имя пишется через дефис, как в HTML, а не в camelCase. Значение атрибута можно вывести прямо в CSS через функцию attr(), например в content у псевдоэлемента.
Пример: подсветка по data-status без единой строчки JS
querySelectorAll ищет по имени атрибута с дефисом, а CSS-селекторы и attr() работают с data-* так же, как с любыми другими атрибутами.
Запись, изменение и удаление через dataset
Через dataset можно не только читать, но и записывать значения: присваивание element.dataset.favorite = 'true' сразу добавляет в разметку настоящий атрибут data-favorite="true", который виден и в outerHTML, и в инспекторе браузера. Удаляется атрибут оператором delete element.dataset.favorite - результат идентичен ручному вызову setAttribute и removeAttribute, просто короче писать.
Пример: добавление и удаление data-атрибута на лету
dataset.favorite = "true" добавляет атрибут в разметку, delete dataset.favorite убирает его - никакой магии, обычный setAttribute/removeAttribute под капотом.
Ловушка с типами: falsy и truthy
Все значения dataset - строки, и это создаёт две похожие, но разные ловушки. Если атрибут записан без значения, как data-active, то dataset.active равен пустой строке '' - а пустая строка в JavaScript ложна, поэтому if (el.dataset.active) не сработает, хотя атрибут присутствует в разметке. Вторая ловушка - противоположная: data-open="false" превращается в dataset.open, равный строке "false", а непустая строка в JavaScript всегда истинна. В обоих случаях нужна явная проверка значения, а не прямая подстановка в условие: el.dataset.active === '' для первого случая и el.dataset.open === "true" для второго.
Частые ошибки с атрибутами data-*
С data-атрибутами чаще всего ошибаются не в синтаксисе HTML, а в том, как их потом читает JavaScript.
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Пишут data-Status с заглавной буквой | Браузер сам приводит имя к нижнему регистру - получается data-status, а не то, что написано | Писать имена data-* только в нижнем регистре с самого начала |
| Ожидают dataset.foo1 от data-foo-1 | Дефис перед цифрой в camelCase не превращается, свойство остаётся undefined | Обращаться через скобки: dataset['foo-1'] |
| Проверяют data-active="" как if (dataset.active) | Пустая строка ложна в JavaScript, хотя атрибут есть в разметке | Сравнивать явно: dataset.active === '' |
| Считают data-open="false" ложным значением | Любая непустая строка, включая "false", истинна в JavaScript | Сравнивать со строкой: dataset.open === 'true' |
| Ищут элемент через querySelector с camelCase именем | querySelector и getAttribute принимают только дефисное имя, как в HTML | Использовать [data-user-id], а не [dataUserId] |
Шпаргалка по атрибутам data-*
| Правило | Что запомнить |
|---|---|
| Синтаксис в HTML | data-имя="значение" - только латиница, цифры, дефисы, точки, двоеточия, подчёркивания |
| Доступ из JS | element.dataset.имяВCamelCase или element.getAttribute('data-имя') |
| Дефис + буква | Превращается в camelCase: data-date-of-birth → dataset.dateOfBirth |
| Дефис + цифра | camelCase НЕ образует: data-foo-1 доступен только как dataset['foo-1'] |
| Тип значения | Всегда строка, даже если выглядит как число или true/false |
| Поиск в CSS/JS | [data-status="done"], [data-status^="error"] - имя пишется с дефисом, не camelCase |
| Вывод в CSS | content: attr(data-status) показывает значение атрибута в псевдоэлементе |
| Запись и удаление | dataset.x = 'y' добавляет атрибут, delete dataset.x убирает его |
| Ловушка falsy | data-active (без значения) → dataset.active === '' - пустая строка ложна |
| Ловушка truthy | data-open="false" → dataset.open === "false" - непустая строка истинна |