Нестандартные свойства DOM-узлов

В наших прошлых занятиях мы не раз отмечали, что узлы DOM-дерева – это объекты языка JavaScript. Но раз это так, значит, мы можем добавлять в них свои собственные свойства. Например, возьмем объект body:

let body = document.body;

и пропишем в нем новое свойство myStyle:

body.myStyle = "color: red";

и отобразим его в консоле:

console.log(body.myStyle);

Видим, что у нашего объекта добавилось новое свойство. Мы можем даже поступить так:

body.myStyle = {
    color: "red",
    fontSize: 20
}
 
console.log(body.myStyle.fontSize);

И теперь, наше свойство ссылается на объект со стилями. Наконец, никто нам не мешает прописать и новый метод для объекта body:

body.getFontSize = function() {
    return this.myStyle.fontSize;
}
 
console.log( body.getFontSize() );

Как видите это все работает, так как DOM-объекты – это обычные объекты JavaScript.

Однако, с добавлением новых свойств и методов нужно быть осторожными. Если какое-либо свойство уже существует и мы пытаемся его изменить, то оно либо изменится на другое или же возникнет ошибка, если свойство неизменяемое. Но как понять: какие свойства есть у DOM-объекта, а каких нет. Один из вариантов – просмотреть существующие свойства с помощью вывода объекта в консоль через метод dir:

console.dir( body );

Некоторые из этих свойств могут быть изменены, а другие – нет. Причем, значения этих свойств присваиваются автоматически браузером при создании DOM-дерева. Например, у нас имеется такая строчка:

<div id="comm">Сообщения от пользователей</div>

Здесь автоматически будет создан объект div со свойством id, равным comm. Можем в этом убедиться, если выведем его в консоль:

let div = document.getElementById("comm");
console.dir( div );

Вообще, всем стандартным атрибутам автоматически присваиваются соответствующие значения. Но, что если мы прописываем в теге какой-нибудь нестандартный атрибут, например:

<div id="comm" deflt="значение">Сообщения</div>

Тогда при попытке его вывести в консоль

console.log( div.deflt );

мы увидим значение undefined. Это говорит о том, что оно не создано в явном виде. Но это не значит, что его нет. Для работы с такими нестандартными свойствами следует пользоваться следующими методами:

  • elem.hasAttribute(name) – проверяет наличие атрибута;
  • elem.getAttribute(name) – получает значение атрибута;
  • elem.setAttribute(name, value) – устанавливает значение атрибута;
  • elem.removeAttribute(name) – удаляет атрибут.

Например:

console.log( div.getAttribute("deflt") );
div.setAttribute("deflt", "новое значение");
console.log( div.getAttribute("deflt") );

А полный список указанных атрибутов в HTML-документе, хранится в коллекции attributes:

for(let val of div.attributes)
    console.log(val);

Здесь тонким моментом является то, что все значения нестандартных атрибутов могут быть только строками: не числами, не объектами, а только строками. Даже если мы указываем какой-то другой тип данных, он автоматически будет приведет к строке. Вот это следует иметь в виду.

Стандартные атрибуты могут принимать другие типы данных. Например, если у нас есть вот такой checkbox:

<input id="input" type="checkbox" checked> checkbox

то при выводе типа свойства checked:

let inp = document.getElementById("input");
console.log( typeof inp.checked );

увидим булевый тип. Хотя большинство стандартных свойств все же являются строками.

Теперь давайте подробнее посмотрим для чего могут быть использованы нестандартные атрибуты. В первом случае они могут помочь нам выбрать необходимые узлы DOM-дерева и прописать им заданные свойства. Например, пусть у нас имеется вот такая таблица:

<table><tr>
   <td cell-red="#CC0000">1</td><td cell-blue="#0000CC">2</td>
   <td cell-red="#CC0000">3</td><td cell-blue="#0000CC">4</td>
</tr>
<tr>
   <td cell-blue="#0000CC">5</td><td cell-red="#CC0000">6</td>
   <td cell-blue="#0000CC">7</td><td cell-red="#CC0000">8</td>
</tr></table>

Для перебора всех ячеек с атрибутом cell-red можно воспользоваться таким скриптом:

for(let cell of document.querySelectorAll('[cell-red]')) {
    let attr = cell.getAttribute('cell-red');
    cell.style.background = attr;
}

Мы здесь с помощью querySelectorAll выбрали все элементы с атрибутом cell-red и, затем, присвоили им указанный цвет фона. В ряде случаев это может быть очень удобно.

Во втором случае мы можем использовать эти атрибуты в таблице стилей, например, так:

<style>
td[cell-red="#CC0000"] {
   background: #CC0000;
}
td[cell-red="lightRed"] {
   background: #FF9999;
}
</style>

И далее, через JavaScript мы можем управлять этими стилями так:

let flSkip = false;
for(let cell of document.querySelectorAll('[cell-red]')) {
   if(!flSkip) cell.setAttribute('cell-red', 'lightRed');
   flSkip = !flSkip;
}

В ряде случаев это добавляет гибкости и элегантности в управлении элементами HTML-документа.

Но здесь, как всегда, есть одна тонкость: что если нестандартный атрибут cell-red появится в будущих версиях языка JavaScript и станет уже стандартным? Это может привести к непредсказуемому поведению нашей программы в будущем. Чтобы такого не было создатели языка JavaScript решили, что все атрибуты, начинающиеся с префикса

data-

будут заведомо восприниматься как нестандартные. Поэтому, наш скрипт лучше переписать так:

<table><tr>
   <td data-cell-red="#CC0000">1</td>
   <td data-cell-blue="#0000CC">2</td>
   <td data-cell-red="#CC0000">3</td>
   <td data-cell-blue="#0000CC">4</td>
</tr>
<tr>
   <td data-cell-blue="#0000CC">5</td>
   <td data-cell-red="#CC0000">6</td>
   <td data-cell-blue="#0000CC">7</td>
   <td data-cell-red="#CC0000">8</td>
</tr></table>

Стили будут выглядеть так:

<style>
td[data-cell-red="#CC0000"] {
    background: #CC0000;
}
td[data-cell-red="lightRed"] {
    background: #FF9999;
}
</style>

И скрипт примет вид:

let flSkip = false;
for(let cell of document.querySelectorAll('[data-cell-red]')) {
    if(!flSkip) cell.setAttribute('data-cell-red', 'lightRed');
    flSkip = !flSkip;
}

Мало того, все эти свойства с префиксом data- становятся доступными через специальную коллекцию dataset, которая имеется у всех DOM-элементов:

if(!flSkip) cell.dataset.cellRed = 'lightRed';

причем, сам префикс data- опускается и оставшееся имя записывается по следующему правилу:

  • если свойство использует дефисы, то в dataset оно прописывается в верблюжьей нотации (например, data-cell-red => cellRed);
  • в остальных случаях просто используется оставшееся имя (например, data-color => color).

Вот так можно добавлять свои собственные свойства, методы и атрибуты в объекты DOM-дерева.

Видео по теме