В течение долгого времени веб-разработчикам приходилось хранить данные об элементах DOM. Одним из наиболее распространенных методов было добавление данных в качестве имен классов. Пуристы, как и я, всегда чувствовали себя неправильно, потому что это не место для данных. Альтернативным способом было добавление пользовательских атрибутов к интересующим элементам. Эта практика приводит к неверной разметке, поскольку пользовательские атрибуты не поддерживаются спецификацией. Итак, вы в конечном итоге пожертвовали валидацией для достижения своей цели. Эта ситуация была очень расстраивающей. К счастью, HTML5 это исправил. Фактически, HTML5 не только представил возможность добавления пользовательских атрибутов через атрибуты данных , но также предоставил API для работы с ними, называемый API набора данных. В этой статье мы узнаем, как работает этот API и что он может сделать для нас.
Что такое API набора данных?
Среди всех новых элементов (таких как article
section
header
footer
High Resolution Time , User Timing , getUserMedia и Page Visility ), HTML5 также представил атрибуты данных и API набора данных. Прежде чем углубляться в наше обсуждение API набора данных, я хочу дать вам краткое представление о том, что такое атрибуты данных.
Атрибуты данных получают свое имя из префикса data-
Это также объясняет, почему иногда их называют атрибутами data-*
Пример элемента, использующего атрибуты данных, показан ниже.
<span id="element" data-level="1" data-points="100" data-opponent="Dragon"></span>
Имена, которые вы можете выбрать, не ограничены одним словом. Имена также могут состоять из нескольких слов, разделенных дефисами ( -
Итак, допустим, вы хотите изменить атрибут opponent
final opponent
Вы бы написали элемент, как показано в следующем примере.
<span id="element" data-level="1" data-points="100" data-final-opponent="Dragon"></span>
Теперь у вас должно быть четкое представление о том, что такое атрибуты данных, поэтому давайте начнем обсуждать API. API набора данных предоставляет нам простой способ работы с атрибутами данных. Этот API позволяет нам устанавливать, получать или даже удалять значения атрибутов данных. API набора данных предоставляет атрибут элемента DOM с именем dataset
DOMStringMap
Ключи этого объекта — это имена атрибутов данных без префикса data-
Соответствующие значения являются значениями атрибутов данных. Если имя атрибута состоит из нескольких слов, разделенных дефисом, оно преобразуется в camelCase. Давайте посмотрим на следующий пример:
var obj = document.getElementById("element").dataset
Предыдущий оператор будет следующим объектом в переменной obj
{
level: "1",
points: "100",
finalOpponent: "Dragon"
}
Доступ к отдельным атрибутам данных можно получить с помощью setAttribute()
getAttribute()
removeAttribute()
Однако API набора данных предоставляет удобный и прямой способ доступа к пользовательским данным. Если API не поддерживается, вы должны получить все атрибуты, а затем отфильтровать те, которые не начинаются с data-
И хотя API набора данных проще, он также медленнее, чем ранее упомянутые методы, что доказано этим JSperf . Однако, если вы не обращаетесь к тысячам атрибутов в секунду, вы не заметите никакой разницы.
Теперь, когда мы обсудили API набора данных, пришло время посмотреть, как мы можем его использовать.
Установка значений
Представьте, что мы хотим добавить атрибут data-media
song
Для выполнения этой задачи мы можем написать следующий код. Обратите внимание, что если атрибут уже определен, его значение перезаписывается.
document.getElementById("element").dataset.media = "song";
Получение ценностей
Создание атрибутов совершенно бесполезно, если мы не можем их получить. Допустим, мы хотим вывести значение атрибута data-final-opponent
Код для этого будет выглядеть так:
console.log(document.getElementById("element").dataset.finalOpponent);
// prints "Dragon"
Удаление атрибутов
Чтобы удалить значение, просто перезапишите его, используя пустую строку. Однако для фактического удаления атрибута мы можем использовать оператор delete
Пример, который удаляет атрибут data-final-opponent
delete document.getElementById("element").dataset.finalOpponent;
После выполнения предыдущего оператора попытка получить значение атрибута приведет к undefined
Совместимость браузера
API набора данных широко поддерживается в настольных и мобильных браузерах, за исключением Internet Explorer, который реализует API только в IE11. Кроме того, есть некоторые старые мобильные браузеры, которые не поддерживают его, но в целом поддержка отличная. Для тех браузеров, которые не поддерживают этот API, доступна поддержка полифилла с именем HTML 5 . Если вы не хотите добавлять polyfill для такого простого API, вы можете использовать setAttribute()
getAttribute()
removeAttribute()
демонстрация
Учиться чему-то новому — это здорово, но еще лучше, если мы сможем поиграть с этим. Итак, мы создадим небольшую демонстрацию, которая позволит нам увидеть, как работает API набора данных. Общая идея состоит в том, чтобы иметь элемент, с помощью которого мы можем получать, устанавливать и удалять атрибуты данных. Чтобы увидеть, что происходит, и следить за текущим состоянием элемента, у нас будет небольшое окно, в которое мы будем записывать сделанные нами изменения. Кроме того, у нас будет область с необработанным HTML-кодом элемента, представляющего его текущее состояние.
Чтобы поиграть с его атрибутами данных, нам нужны два поля ввода: ключ и значение. Первый позволяет нам установить имя атрибута, который мы хотим сохранить, а второй — где мы напишем значение атрибута. Поскольку мы хотим разрешить три различных действия (получить, установить и удалить), нам также понадобятся три кнопки, к которым мы добавим обработчики. Как всегда, мы также проверим поддержку браузера, а если тест не пройден, мы покажем сообщение «API не поддерживается».
Прежде чем показывать вам демонстрационный код, я хочу поделиться с вами двумя соображениями. Демо предполагает, что вы прочитали всю статью. Следовательно, вы знаете, что для выполнения действия с атрибутом данных, имя которого содержит более одного слова, вам необходимо преобразовать это имя в camelCase. Если вы хотите изменить это поведение и иметь возможность писать «конечный оппонент» вместо «конечный оппонент», я написал две служебные функции для вас. Чтобы использовать эти функции, вам нужно добавить их в демонстрационную версию и изменить код, чтобы вы вызывали их внутри обработчиков перед выполнением действия. Исходный код этих функций приведен ниже.
function hyphenToCamelCase(string) {
return string.replace(/-([a-z])/g, function(string) {
return string[1].toUpperCase();
});
}
function camelCaseToHyphen(string) {
return string.replace(/([A-Z])/g, function(string) {
return '-' + string.toLowerCase();
});
}
Второе, что нужно иметь в виду, это то, что до сих пор мы обращались к атрибутам данных с помощью оператора точки, потому что знали его имя заранее. В демоверсии у нас нет этой информации, поэтому для доступа к свойствам dataset
Теперь пришло время показать вам исходный код. Как обычно, живая демонстрация кода доступна здесь .
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Dataset API Demo</title>
<style>
body
{
max-width: 500px;
margin: 2em auto;
font-size: 20px;
}
h1
{
text-align: center;
}
.hidden
{
display: none;
}
#log
{
height: 200px;
width: 100%;
overflow-y: scroll;
border: 1px solid #333333;
line-height: 1.3em;
}
.buttons-demo-wrapper
{
text-align: center;
}
.button-demo
{
padding: 0.5em;
margin: 1em;
}
.author
{
display: block;
margin-top: 1em;
}
</style>
</head>
<body>
<h1>Dataset API</h1>
<h3>Live sample element</h3>
<div id="showcase">
<span id="play-element" class="hidden" data-level="1" data-points="100" data-final-opponent="Dragon"></span>
</div>
<h3>Play area</h3>
<div>
<label for="key">Key:</label>
<input type="text" id="key"></input>
<label for="value">Value:</label>
<input type="text" id="value"></input>
<div class="buttons-demo-wrapper">
<button id="set-data" class="button-demo">Set data</button>
<button id="get-data" class="button-demo">Get data</button>
<button id="delete-data" class="button-demo">Delete data</button>
</div>
</div>
<span id="d-unsupported" class="hidden">API not supported</span>
<h3>Log</h3>
<div id="log"></div>
<button id="clear-log" class="button-demo">Clear log</button>
<span id="play-element" class="hidden" data-level="1" data-points="100" data-final-opponent="Dragon"></span>
<script>
if (!"dataset" in document.createElement("span")) {
document.getElementById("d-unsupported").classList.remove("hidden");
["set-data", "get-data", "delete-data"].forEach(function(elementId, index) {
document.getElementById(elementId).setAttribute("disabled", "disabled");
});
} else {
var playElement = document.getElementById("play-element");
var key = document.getElementById("key");
var value = document.getElementById("value");
var log = document.getElementById("log");
var showcase = document.getElementById("showcase");
document.getElementById("clear-log").addEventListener("click", function() {
log.innerHTML = "";
});
document.getElementById("set-data").addEventListener("click", function() {
if (key.value.indexOf("-") !== -1) {
log.innerHTML = "Warning! Hyphen not allowed. Use camelCase instead.\n" + log.innerHTML;
} else {
playElement.dataset[key.value] = value.value;
showcase.textContent = playElement.outerHTML;
log.innerHTML = "Set data-" + key.value + " attribute to '" + value.value + "'<br />" + log.innerHTML;
}
});
document.getElementById("get-data").addEventListener("click", function() {
if (key.value.indexOf("-") !== -1) {
log.innerHTML = "Warning! Hyphen not allowed. Use camelCase instead.<br />" + log.innerHTML;
} else {
log.innerHTML = "Get data-" + key.value + " attribute. Value: '" + playElement.dataset[key.value] + "'<br />" + log.innerHTML;
}
});
document.getElementById("delete-data").addEventListener("click", function() {
if (key.value.indexOf("-") !== -1) {
log.innerHTML = "Warning! Hyphen not allowed. Use camelCase instead.<br />" + log.innerHTML;
} else {
delete playElement.dataset[key.value];
showcase.textContent = playElement.outerHTML;
log.innerHTML = "Deleted data-" + key.value + " attribute<br />" + log.innerHTML;
}
});
}
</script>
</body>
</html>
Выводы
В этой статье мы обсудили API набора данных и то, как он может помочь нам хранить пользовательские атрибуты в элементах DOM. Как мы уже видели, API очень прост в использовании. Кроме того, он также широко поддерживается в настольных и мобильных браузерах. Вы можете начать использовать его прямо в следующем проекте. В случае, если у вас есть какие-либо сомнения, я советую вам поиграть с предоставленной демоверсией или оставить вопрос