Представляем вашему вниманию перевод статьи, посвящённой использованию атрибутов data-*
. Это — атрибуты, которые можно применять для удобного хранения в стандартных HTML-элементах различной полезной информации. Эта информация, в частности, может применяться в JavaScript и CSS.
Общие сведения
HTML-элементы могут иметь атрибуты, которые используются для решения самых разных задач — от предоставления данных ассистивным технологиям, до стилизации элементов.
<!-- Атрибут `class` можно использовать для стилизации в CSS, атрибут `role` используется ассистивными технологиями -->
<div class="names" role="region" aria-label="Names"></div>
При этом не рекомендуется создавать собственные атрибуты, или применять существующие атрибуты такими способами, на которые они не рассчитаны.
<!-- `highlight` не является HTML-атрибутом-->
<div highlight="true"></div>
<!-- `large` не является допустимым значением для `width` -->
<div width="large">
Это плохо по многим причинам. HTML-код оказывается неправильно сформированным. И хотя подобное может и не иметь реальных негативных последствий, это лишает разработчика тёплого чувства вызываемого тем, что он создаёт валидный HTML-код. Но главная причина того, что так поступать не следует, заключается в том, что HTML — это язык, который постоянно развивается. В результате, если некий атрибут сегодня в языке не применяется, это не значит, что в будущем ничего не изменится.
Правда, если кому-то нужно использовать подобные атрибуты, у него есть совершенно нормальный способ это делать. Речь идёт о создании собственных атрибутов, названия которых начинаются с префикса data-
. С этими атрибутами можно совершенно спокойно работать, применяя их так, как нужно программисту.
Синтаксис
Возможность создавать собственные HTML-атрибуты и записывать в них данные может оказаться крайне полезной. Это, как вы понимаете, возможно благодаря атрибутам data-*
. Именно для этого такие атрибуты и предназначены. Выглядит это так:
<!-- Этим атрибутам необязательно назначать значения -->
<div data-foo></div>
<!-- ...но они могут содержать значения -->
<div data-size="large"></div>
<!-- Тут мы имеем дело с HTML, поэтому надо экранировать HTML-код, который, возможно, решено будет записать в атрибут -->
<li data-prefix="Careful with HTML in here."><li>
<!-- Если надо - можно создавать длинные имена атрибутов -->
<aside data-some-long-attribute-name><aside>
Эти атрибуты, именно из-за того, что они всегда начинаются с префикса data-
, часто называют атрибутами data-*
или data-атрибутами. При формировании имён этих атрибутов сначала идёт слово data
, потом — тире (-
), а потом — оставшаяся часть имени, устроенная так, как нужно разработчику.
Можно ли использовать атрибут, имеющий имя data?
Вот пример кода, в котором используется атрибут с именем data
:
<div data=""></div>
Атрибут с таким именем, вероятно, никому не повредит, но его применение не позволит пользоваться JavaScript-инструментами, которые мы рассмотрим ниже. В этом примере разработчик, фактически, создаёт некий собственный атрибут, что, как уже было сказано, делать не рекомендуется.
Чего не стоит делать с атрибутами data-*
В таких атрибутах не стоит хранить содержимое, которое должно быть доступно ассистивным технологиям. Если некие данные должны быть видимы на странице, или должны быть доступны средствам для чтения с экрана, их недостаточно просто поместить в атрибут data-*
. Такие данные должны появиться и в обычной HTML-разметке.
<!-- Эти данные не выводятся на странице, они недоступны ассистивным технологиям -->
<div data-name="Chris Coyier"></div>
<!-- Если нужен программный доступ к данным, но они не должны выводиться на странице, есть и другие способы... -->
<div>
<span class="visually-hidden">Chris Coyier</span>
</div>
Вот материал о том, как скрывать элементы веб-страниц.
Стилизация элементов с использованием атрибутов data-*
В CSS можно выбирать HTML-элементы, основываясь на атрибутах и их значениях.
/* Выбрать элемент с таким именем атрибута, имеющим такое значение */
[data-size="large"] {
padding: 2rem;
font-size: 125%;
}
/* Выбор можно ограничить элементом, классом, или чем-то другим */
button[data-type="download"] { }
.card[data-pad="extra"] { }
Это может показаться интересным. Для стилизации в HTML/CSS используются, в основном, классы. И хотя классы — это замечательный инструмент (они отличаются средним уровнем специфичности, с ними можно работать с помощью удобных JavaScript-методов через свойство элементов classList
), элемент может либо иметь, либо не иметь некоего класса (то есть, класс в элементе либо «включен», либо «выключен»). При использовании атрибутов data-*
в распоряжении разработчика оказываются и возможности классов («включено/выключено»), и возможность выбора элементов, основываясь на значении атрибута, которое он имеет на том же уровне специфичности.
/* Выбор элементов, у которых имеется указанный атрибут */
[data-size] { }
/* Выбор элемента, атрибут которого имеет заданное значение */
[data-state="open"],
[aria-expanded="true"] { }
/* Селектор "начинается с", использование которого приведёт к выбору элементов, атрибут которых содержит "3", а так же - что угодно другое, начинающееся с 3 - вроде "3.14" */
[data-version^="3"] { }
/* Селектор "содержит" указывает на то, что заданная строка должна содержаться где-то в значении свойства */
[data-company*="google"] { }
Специфичность селекторов атрибутов
Специфичность селекторов атрибутов такая же, как у классов. Специфичность часто рассматривают как значение, состоящее из 4 частей:
- Встроенный стиль
- ID
- Классы и атрибуты
- Теги
В результате, если представить себе значение специфичности для элемента, который стилизуется только с использованием селектора атрибута, то оно будет выглядеть так: 0, 0, 1, 0
.
А вот ещё один селектор:
div.card[data-foo="bar"] { }
Он уже будет описываться значением 0, 0, 2, 1
. Число 2
здесь появляется из-за того, что тут имеется и класс (.card
), и атрибут ([data-foo="bar"]
). А 1
здесь из-за того, что тут присутствует лишь один тег (div
).
Вот, чтобы было понятнее, иллюстрированная версия этих рассуждений.
1 тег, 1 класс и 1 атрибут
У селекторов атрибутов специфичность ниже, чем у идентификаторов (ID
), но выше, чем у тегов (элементов). Их специфичность равна специфичности классов.
Значения атрибутов, нечувствительные к регистру
Если нужно, чтобы селекторы выбирали бы атрибуты, значения которых могут содержать строки, записанные с использованием разных комбинаций строчных и прописных букв, можно воспользоваться вариантом селектора, нечувствительным к регистру.
/* Всё это соответствует следующему селектору
<div data-state="open"></div>
<div data-state="Open"></div>
<div data-state="OPEN"></div>
<div data-state="oPeN"></div>
*/
[data-state="open" i] { }
Такое поведение обеспечивает использование в селекторе символа i
.
Вывод данных, хранящихся в атрибутах data-*
CSS позволяет извлекать значения атрибутов data-*
и выводить их на странице.
/* <div data-emoji=":-)"> */
[data-emoji]::before {
content: attr(data-emoji); /* Возвращает ':-)' */
margin-right: 5px;
}
Примеры использования атрибутов data-* для стилизации элементов
Атрибуты data-*
можно использовать для указания того, сколько столбцов должно быть у grid
-контейнера. Вот HTML-код:
<div data-columns="2">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div data-columns="3">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div data-columns="4">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
Вот соответствующий CSS-код:
[data-columns] {
display: grid;
grid-gap: 1rem;
padding: 1rem;
margin: 0 0 1rem 0;
}
[data-columns] > div {
height: 100px;
background: white;
}
[data-columns="2"] {
background: #64B5F6;
grid-template-columns: repeat(2, 1fr);
}
[data-columns="3"] {
background: #9CCC65;
grid-template-columns: repeat(3, 1fr);
}
[data-columns="4"] {
background: #FFB74D;
grid-template-columns: repeat(4, 1fr);
}
А вот — фрагмент результирующей страницы.
Grid-контейнеры, настройка которых производится с помощью атрибутов data-*
Поэкспериментировать с этим примером можно на CodePen.
Работа с атрибутами data-* в JavaScript
К значениям атрибутов data-*
можно обращаться, как и к значениям других атрибутов, пользуясь методом getAtribute
для чтения данных, и методом setAttribute
для записи.
// Чтение значения атрибута
let value = el.getAttribute("data-state");
// Запись значения атрибута.
// В data-state записывается значение "collapsed"
el.setAttribute("data-state", "collapsed");
Однако атрибуты data-*
имеют и собственный особый API. Предположим, у нас есть элемент с несколькими атрибутами data-*
(что совершенно нормально):
<span
data-info="123"
data-index="2"
data-prefix="Dr. "
data-emoji-icon=":-)"
></span>
Если имеется ссылка на этот элемент, то читать и записывать значения его атрибутов можно так:
// Чтение
span.dataset.info; // 123
span.dataset.index; // 2
// Запись
span.dataset.prefix = "Mr. ";
span.dataset.emojiIcon = ";-)";
Обратите внимание на то, что в последней строке JS-кода используется запись имени атрибута в верблюжьем стиле. Система автоматически преобразует имена HTML-атрибутов, записанные в кебаб-стиле, в имена, записанные в верблюжьем стиле. То есть — data-this-little-piggy
превращается в dataThisLittlePiggy
.
Этот API, конечно, не такой удобный, как API classList, поддерживающий понятные методы вроде add
, remove
, toggle
и replace
, но это, всё же, лучше чем ничего.
В JavaScript можно работать и со встроенными в элементы наборами данных:
<img align="center" src="spaceship.png"
data-ship-id="324" data-shields="72%"
onclick="pewpew(this.dataset.shipId)">
</img>
<h2><font color="#3AC1EF">JSON-данные в атрибутах data-*</font></h2>
<ul>
<li data-person='
{
"name": "Chris Coyier",
"job": "Web Person"
}
'></li>
</ul>
А почему бы не записать в атрибут data-*
JSON-данные? Ведь это всего лишь строки, которые можно отформатировать как валидные JSON-данные (учитывая кавычки и прочее). При необходимости эти данные можно извлечь из атрибута и распарсить.
const el = document.querySelector("li");
let json = el.dataset.person;
let data = JSON.parse(json);
console.log(data.name); // Chris Coyier
console.log(data.job); // Web Person
Об использовании атрибутов data-* в JavaScript
Идея тут заключается в том, чтобы, пользуясь атрибутами data-*
, размещать в HTML-коде данные, к которым можно обращаться из JavaScript для выполнения неких действий.
Распространённый вариант реализации этого сценария направлен на организацию работы с базами данных. Предположим, у нас имеется кнопка Like
:
<button data-id="435432343">Like</button>
У этой кнопки может быть обработчик нажатия, выполняющий AJAX-запрос к серверу. Обработчик, если пользователь лайкнет что-то с помощью кнопки, увеличивает количество лайков в серверной базе данных. Обработчик знает о том, какую именно запись надо обновить, так как берёт сведения об этом из атрибута data-*
.
Итоги
Вот, вот и вот — стандарты, связанные с селекторами атрибутов data-*
. Здесь, на сайте Caniuse, можно узнать о поддержке атрибутов data-*
браузерами. Если вы раньше не пользовались этими атрибутами, надеемся, этот материал дал вам пищу для размышлений.
Уважаемые читатели! Как вы применяете HTML-атрибуты data-*?
Автор: ru_vds