Атрибуты и DOM-свойства
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/dom-attributes-and-properties.
При чтении HTML браузер генерирует DOM-модель. При этом большинство стандартных HTML-атрибутов становятся свойствами соответствующих объектов.
Например, если тег выглядит как <body >, то у объекта будет свойство body.id = "page" .
Но это преобразование – не один-в-один. Бывают ситуации, когда атрибут имеет одно значение, а свойство – другое. Бывает и так, что атрибут есть, а свойства с таким названием не создаётся.
Если коротко – HTML-атрибуты и DOM-свойства обычно, но не всегда соответствуют друг другу, нужно понимать, что такое свойство и что такое атрибут, чтобы работать с ними правильно.
Свои DOM-свойства
Ранее мы видели некоторые встроенные свойства DOM-узлов. Но, технически, никто нас ими не ограничивает.
Узел DOM – это объект, поэтому, как и любой объект в JavaScript, он может содержать пользовательские свойства и методы.
Например, создадим в document.body новое свойство и запишем в него объект:
Можно добавить и новую функцию:
Нестандартные свойства и методы видны только в JavaScript и никак не влияют на отображение соответствующего тега.
Обратим внимание, пользовательские DOM-свойства:
- Могут иметь любое значение.
- Названия свойств чувствительны к регистру.
- Работают за счёт того, что DOM-узлы являются объектами JavaScript.
Атрибуты
Элементам DOM, с другой стороны, соответствуют HTML-теги, у которых есть текстовые атрибуты.
Конечно, здесь речь именно об узлах-элементах, не о текстовых узлах или комментариях.
Доступ к атрибутам осуществляется при помощи стандартных методов:
- elem.hasAttribute(name) – проверяет наличие атрибута
- elem.getAttribute(name) – получает значение атрибута
- elem.setAttribute(name, value) – устанавливает атрибут
- elem.removeAttribute(name) – удаляет атрибут
Эти методы работают со значением, которое находится в HTML.
Также все атрибуты элемента можно получить с помощью свойства elem.attributes , которое содержит псевдо-массив объектов типа Attr.
В отличие от свойств, атрибуты:
- Всегда являются строками.
- Их имя нечувствительно к регистру (ведь это HTML)
- Видны в innerHTML (за исключением старых IE)
Рассмотрим отличия между DOM-свойствами и атрибутами на примере HTML-кода:
Пример ниже устанавливает атрибуты и демонстрирует их особенности.
При запуске кода выше обратите внимание:
- getAttribute(‘About’) – первая буква имени атрибута About написана в верхнем регистре, а в HTML – в нижнем, но это не имеет значения, так как имена нечувствительны к регистру.
- Мы можем записать в атрибут любое значение, но оно будет превращено в строку. Объекты также будут автоматически преобразованы.
- После добавления атрибута его можно увидеть в innerHTML элемента.
- Коллекция attributes содержит все атрибуты в виде объектов со свойствами name и value .
Когда полезен доступ к атрибутам?
Когда браузер читает HTML и создаёт DOM-модель, то он создаёт свойства для всех стандартных атрибутов.
Например, свойства тега ‘A’ описаны в спецификации DOM: HTMLAnchorElement.
Например, у него есть свойство "href" . Кроме того, он имеет "id" и другие свойства, общие для всех элементов, которые описаны в спецификации в HTMLElement.
Все стандартные свойства DOM синхронизируются с атрибутами, однако не всегда такая синхронизация происходит 1-в-1, поэтому иногда нам нужно значение именно из HTML, то есть атрибут.
Рассмотрим несколько примеров.
Ссылка «как есть» из атрибута href
Синхронизация не гарантирует одинакового значения в атрибуте и свойстве.
Для примера, посмотрим, что произойдёт с атрибутом "href" при изменении свойства:
Это происходит потому, что атрибут может быть любым, а свойство href , в соответствии со спецификацией W3C, должно быть полной ссылкой.
Стало быть, если мы хотим именно то, что в HTML, то нужно обращаться через атрибут.
Кстати, есть и другие атрибуты, которые не копируются в точности. Например, DOM-свойство input.checked имеет логическое значение true/false , а HTML-атрибут checked – любое строковое, важно лишь его наличие.
Работа с checked через атрибут и свойство:
Исходное значение value
Изменение некоторых свойств обновляет атрибут. Но это скорее исключение, чем правило.
Чаще синхронизация – односторонняя: свойство зависит от атрибута, но не наоборот.
Например, при изменении свойства input.value атрибут input.getAttribute(‘value’) не меняется:
То есть, изменение DOM-свойства value на атрибут не влияет, он остаётся таким же.
А вот изменение атрибута обновляет свойство:
Эту особенность можно красиво использовать.
Получается, что атрибут input.getAttribute(‘value’) хранит оригинальное (исходное) значение даже после того, как пользователь заполнил поле и свойство изменилось.
Например, можно взять изначальное значение из атрибута и сравнить со свойством, чтобы узнать, изменилось ли значение. А при необходимости и перезаписать свойство атрибутом, отменив изменения.
Классы в виде строки: className
Атрибуту "class" соответствует свойство className .
Так как слово "class" является зарезервированным словом в JavaScript, то при проектировании DOM решили, что соответствующее свойство будет называться className .
Кстати, есть и другие атрибуты, которые называются иначе, чем свойство. Например, атрибуту for ( <label for=". "> ) соответствует свойство с названием htmlFor .
Классы в виде объекта: classList
Атрибут class – уникален. Ему соответствует аж целых два свойства!
Работать с классами как со строкой неудобно. Поэтому, кроме className , в современных браузерах есть свойство classList .
Свойство classList – это объект для работы с классами.
Оно поддерживается в IE начиная с IE10, но его можно эмулировать в IE8+, подключив мини-библиотеку classList.js.
- elem.classList.contains("class") – возвращает true/false , в зависимости от того, есть ли у элемента класс class .
- elem.classList.add/remove("class") – добавляет/удаляет класс class
- elem.classList.toggle("class") – если класса class нет, добавляет его, если есть – удаляет.
Кроме того, можно перебрать классы через for , так как classList – это псевдо-массив.
Нестандартные атрибуты
У каждого элемента есть некоторый набор стандартных свойств, например для <a> это будут href , name , а для <img> это будут src , alt , и так далее.
Точный набор свойств описан в стандарте, обычно мы более-менее представляем, если пользуемся HTML, какие свойства могут быть, а какие – нет.
Для нестандартных атрибутов DOM-свойство не создаётся.
Свойство является стандартным, только если оно описано в стандарте именно для этого элемента.
То есть, если назначить элементу <img> атрибут href , то свойство img.href от этого не появится. Как, впрочем, и если назначить ссылке <a> атрибут alt :
Нестандартные атрибуты иногда используют для CSS.
В примере ниже для показа «состояния заказа» используется атрибут order-state :
Почему именно атрибут? Разве нельзя было сделать классы .order-state-new , .order-state-pending , order-state-canceled ?
Конечно можно, но манипулировать атрибутом из JavaScript гораздо проще.
Например, если нужно отменить заказ, неважно в каком он состоянии сейчас – это сделает код:
Для классов – нужно знать, какой класс у заказа сейчас. И тогда мы можем снять старый класс, и поставить новый:
…То есть, требуется больше исходной информации и надо написать больше букв. Это менее удобно.
Проще говоря, значение атрибута – произвольная строка, значение класса – это «есть» или «нет», поэтому естественно, что атрибуты «мощнее» и бывают удобнее классов как в JS так и в CSS.
Свойство dataset, data-атрибуты
С помощью нестандартных атрибутов можно привязать к элементу данные, которые будут доступны в JavaScript.
Как правило, это делается при помощи атрибутов с названиями, начинающимися на data- , например:
Стандарт HTML5 специально разрешает атрибуты data-* и резервирует их для пользовательских данных.
При этом во всех браузерах, кроме IE10-, к таким атрибутам можно обратиться не только как к атрибутам, но и как к свойствам, при помощи специального свойства dataset :
Обратим внимание – название data-user-location трансформировалось в dataset.userLocation . Дефис превращается в большую букву.
Полифил для атрибута hidden
Для старых браузеров современные атрибуты иногда нуждаются в полифиле. Как правило, такой полифил включает в себя не только JavaScript, но и CSS.
Этот атрибут должен прятать элемент, действие весьма простое, для его поддержки в HTML достаточно такого CSS:
Если запустить в IE11- пример выше, то <div hidden> будет скрыт, а вот последний div , которому поставили свойство hidden в JavaScript – по-прежнему виден.
Это потому что CSS «не видит» присвоенное свойство, нужно синхронизировать его в атрибут.
Вот так – уже работает:
«Особенности» IE8
Если вам нужна поддержка этих версий IE – есть пара нюансов.
Во-первых, версии IE8- синхронизируют все свойства и атрибуты, а не только стандартные:
При этом даже тип данных не меняется. Атрибут не становится строкой, как ему положено.
Ещё одна некорректность IE8-: для изменения класса нужно использовать именно свойство className , вызов setAttribute(‘class’, . ) не сработает.
Вывод из этого довольно прост – чтобы не иметь проблем в IE8, нужно использовать всегда только свойства, кроме тех ситуаций, когда нужны именно атрибуты. Впрочем, это в любом случае хорошая практика.
Итого
- Атрибуты – это то, что написано в HTML.
- Свойство – это то, что находится внутри DOM-объекта.
Таблица сравнений для атрибутов и свойств:
Свойства | Атрибуты |
---|---|
Любое значение | Строка |
Названия регистрозависимы | Не чувствительны к регистру |
Не видны в innerHTML | Видны в innerHTML |
Синхронизация между атрибутами и свойствами:
- Стандартные свойства и атрибуты синхронизируются: установка атрибута автоматически ставит свойство DOM. Некоторые свойства синхронизируются в обе стороны.
- Бывает так, что свойство не совсем соответствует атрибуту. Например, «логические» свойства вроде checked , selected всегда имеют значение true/false , а в атрибут можно записать произвольную строку.Выше мы видели другие примеры на эту тему, например href .
- Нестандартный атрибут (если забыть глюки старых IE) никогда не попадёт в свойство, так что для кросс-браузерного доступа к нему нужно обязательно использовать getAttribute .
- Атрибуты, название которых начинается с data- , можно прочитать через dataset . Эта возможность не поддерживается IE10-.
Для того, чтобы избежать проблем со старыми IE, а также для более короткого и понятного кода старайтесь везде использовать свойства, а атрибуты – только там, где это действительно нужно.
А действительно нужны атрибуты очень редко – лишь в следующих трёх случаях:
- Когда нужно кросс-браузерно получить нестандартный HTML-атрибут.
- Когда нужно получить «оригинальное значение» стандартного HTML-атрибута, например, <input value=". "> .
- Когда нужно получить список всех атрибутов, включая пользовательские. Для этого используется коллекция attributes .
Если вы хотите использовать собственные атрибуты в HTML, то помните, что атрибуты с именем, начинающимся на data- валидны в HTML5 и современные браузеры поддерживают доступ к ним через свойство dataset .
Как работат метод dataset в js?
Свойство .dataset , позволяет считывать, или устанавливать любые дата-атрибуты на HTML-элементе. Дата атрибуты используются, чтобы хранить значения на элементах в HTML.
Обращение к свойству .dataset вернёт объект со всеми дата-атрибутами, которые есть на элементе.
Если дата-атрибутов на элементе нет, то вернётся пустой объект:
Добавляем дата-атрибут к элементу. Название поля должно быть без префикса data-, браузер автоматически подставит его. Атрибуты состоящие из нескольких слов записываются в .dataset c помощью camelСase, а в HTML будут иметь названия в kebab-case. В значениях атрибутов в HTML могут быть только строки, поэтому любое значение будет автоматически приведено к строке.
Удалить дата-атрибут можно с помощью оператора delete. Возьмем предыдущий html
Data Attributes In JavaScript
One of the best ways to store data in HTML is with data attributes. These data attributes can be used to do some pretty cool things in CSS without the need for JavaScript, as seen in this article, but data attributes are most useful when combined with JavaScript. In this article I will teach you exactly how to use data attributes in JavaScript and what makes them so powerful.
Data Attribute Introduction
To get started talking about data attributes we need to first have some HTML with data attributes. To create a data attribute in HTML we just need to add a custom attribute to our HTML element that starts with data- .
Reading Data Attributes
We now have a div with three custom data attributes. Now let’s move over to JavaScript to see how we would access these data attributes.
The dataset property on an element will return a DOMStringMap which is essentially just an object that contains all the custom data attributes of an element. Our dataset looks like this.
You will notice two interesting things about this.
First, all of our properties are converted from snake case, first-name , to camel case, firstName . This is because in JavaScript object properties are primarily written as camel case so this just makes working with the JavaScript object much easier.
Second, the active property has a value of "" . This is because any data attribute without a value is assumed to have an empty string as its value.
Now in order to access an individual data attribute we just access it like a property on an object since dataset is just an object.
Writing Data Attributes
In order to create a new data attribute in JavaScript we just need to add a new property to the dataset object with a value.
This will update the dataset object and our HTML which means our HTML will look like this.
Updating Data Attributes
Let’s say that we now want to update the value of a data attribute. This is incredibly easy since it works just like a normal object. We just need to set the value of our dataset property to the new value and it will update the HTML for us.
This will update the dataset object and our HTML which means our HTML will look like this.
Delete Data Attributes
Deleting data attributes is a bit different since we need to actually remove the property from our object. This is because if we try setting the value to undefined or null the dataset object will still have a reference to that property with that value of undefined or null and will set the value of our HTML data attribute to the string null or undefined .
To delete an element we need to use the delete keyword to remove it completely from the object.
This will update the dataset object and our HTML which means our HTML will look like this.
Real World Example
Now let’s combine all this into a real world example. Let’s say you have the following HTML.
You want to write JavaScript so that the first button opens modal 1 and the second button opens modal 2, but we want to do this in a way that is reusable so if we add a third button that opens a new modal we don’t need to write any new JavaScript code.
This may sound really difficult at first, but essentially all we need is some way to link each button to their respective modal in the HTML. This is where data attributes come in.
We can set a custom data attribute on each button that references the modal they are linked to. In our case we can use the id of each modal as our reference.
So now we have a way to access the id of the modal linked to each button inside JavaScript.
In the above code we are selecting all elements that contain our custom data-modal-id attribute. We are then looping through them and adding a click event listener to each one. Inside this event listener we are using the modal id to get the modal link to that button and adding the show class so it is now visible.
This code is also flexible since it will get any element with the custom data-modal-id attribute. This means that if we add a new button that references a new modal we won’t need to write any additional JavaScript.
Conclusion
Data attributes in JavaScript are incredibly useful. They allow you to write extremely flexible code which means you can spend more time writing the HTML for your project and less time worrying about writing a custom event listener for each new element you add.
How to remove data-* attributes using the HTML dataset attribute
According to the dataset specification, how is element.dataset meant to delete data attributes? Consider:
the DOM becomes this in Chrome and Firefox:
The Chrome/Firefox implementations mimic setAttribute. It basically applies .toString() first. This makes sense to me except for the treatment of null because I would expect that null would remove the attribute. Otherwise how does the dataset API do the equivalent of:
And what about boolean attributes:
<p data-something> is equivalent to <p data-something=""> Hmm.