У DOM-узлов бывают не только свойства, но и атрибуты.
Между ними есть кое-что общее, поэтому иногда их путают. Но на самом деле, атрибуты и свойства — совершенно разные вещи.
Свойства
Узел DOM - это объект. Как и любой объект JavaScript, он может содержать пользовательские свойства и методы.
Например, создадим в document.body новое свойство и запишем в него объект:
document.body.myData = {
name: 'Петр',
familyName: 'Петрович'
};
alert(document.body.myData.name); // Петр
Можно добавить и новую функцию:
document.body.sayHi = function() {
alert(this.nodeName);
}
document.body.sayHi(); // BODY, выполнилась с правильным this
Такие свойства и методы видны только в JavaScript и никак не влияют на отображение соответствующего тега.
Эти свойства можно даже перечислить с помощью for..in (свойства будут все, и встроенные тоже):
document.body.custom = 5;
var list = [];
for (var key in document.body) {
list.push(key);
}
alert( list.join('\n') );
Пользовательские DOM-свойства:
- Могут иметь любое значение.
- Названия свойств чувствительны к регистру.
- Работают за счет того, что DOM-узлы являются объектами JavaScript
Атрибуты
Узлы DOM, с другой стороны, являются HTML-элементами, у которых есть атрибуты.
Доступ к атрибутам осуществляется при помощи стандартных методов:
elem.hasAttribute(name)- проверяет наличие атрибутаelem.getAttribute(name)- получает значение атрибутаelem.setAttribute(name, value)- устанавливает атрибутelem.removeAttribute(name)- удаляет атрибут
Атрибуты неправильно работают в IE<8 и в IE8 в режиме совместимости:
- Существуют только методы
getAttributeиsetAttribute. - Фактически, они изменяют DOM-свойства, а не атрибуты.
- Атрибуты и свойства в IE<8 объединены. Иногда это приводит к странным результатам, но способы работы с атрибутами, о которых мы здесь говорим, работают правильно.
В отличие от свойств, атрибуты:
- Могут быть только строками.
- Их имя нечувствительно к регистру(т.к. это HTML)
- Они отражены в HTML, включая свойство
innerHTML(за исключением старых IE) - Все атрибуты элемента можно получить с помощью свойства
attributes, которое содержит псевдо-массив объектов типа Attr.
Например, рассмотрим этот HTML-код:
<body> <div about="Elephant" class="smiling"></div> </body>
Пример ниже устанавливает атрибуты и демонстрирует их особенности.
<body>
<div about="Elephant" class="smiling"></div>
<script>
var div = document.body.children[0];
alert( div.getAttribute('ABOUT') ); // (1)
div.setAttribute('Test', 123); // (2)
alert( document.body.innerHTML ); // (3)
var attrs = div.attributes; // (4)
for (var i=0; i<attrs.length; i++) {
alert(attrs[i].name + " = " + attrs[i].value);
}
</script>
</body>
При запуске кода выше, обратите внимание:
getAttribute('ABOUT')использует имя атрибута в верхнем регистре, но это не имеет значения, т.к имена нечувствительны к регистру.- Вы можете записать в атрибут строку или другое значение, которые будет превращено в строку. Объект, например, будет автоматически сконвертирован, но у IE с этим проблемы, поэтому, придерживайтесь примитивов.
- В
innerHTMLпоявился новый атрибут"test". - Коллекция
attributesсодержит все атрибуты в виде объектов классаAttrсо свойствамиnameиvalue.
Синхронизация свойств и атрибутов
Каждый тип узлов DOM имеет свои стандартные свойства.
Например, свойства тега 'A' описаны в спецификации DOM: HTMLAnchorElement.
Например, у него есть свойство "href". Кроме того, он имеет "id" и другие свойства, общие для всех элементов, которые описаны в спецификации в HTMLElement.
Стандартные свойства DOM синхронизируются с атрибутами.
id
Например, браузер синхронизирует атрибут "id" со свойством id. Изменим одно - поменяется и другое:
<script>
document.body.setAttribute('id','la-la-la');
alert(document.body.id); // la-la-la
</script>
href
Синхронизация не гарантирует одинакового значения. Для примера, посмотрим что произойдет с атрибутом "href" при изменении свойства:
<a href="#"></a>
<script>
var a = document.body.children[0];
*!*
a.href = '/';
*/!*
alert( 'атрибут:' + a.getAttribute('href') ); // '/'
alert( 'свойство:' + a.href ); // *!*полный URL*/!* (в старых IE '/')
</script>
Это происходит потому, что атрибут может быть любым, а свойство href, в соответствии со спецификацией W3C, должно быть полной ссылкой.
Есть и другие свойства-атрибуты, которые не копируются в точности. Например, таково свойство input.checked:
<input type="checkbox" checked>
<script>
var input = document.body.children[0];
alert( input.checked ); // true <-- может быть только true/false
alert( input.getAttribute('checked') ); // пустая строка
</script>
value
А еще есть встроенные свойства, которые синхронизируются в одну сторону.
Например, input.value синхронизируется только из атрибута в свойство:
<body>
<input type="text" value="markup">
<script>
var input = document.body.children[0];
input.setAttribute('value', 'new');
alert( input.value ); // 'new', input.value изменено
</script>
</body>
Но атрибут не синхронизируется из свойства:
<body>
<input type="text" value="markup">
<script>
var input = document.body.children[0];
input.value = 'new';
alert(input.getAttribute('value')); // 'markup', не изменилось!
</script>
</body>
В результате атрибут value хранит оригинальное (исходное) значение даже после того, как пользователь заполнил поле и свойство изменилось. Текущее же значение хранится в свойстве value.
Изначальное значение может использоваться для проверки, было ли изменено значение input и, при необходимости, для того, чтобы отменить изменения.
class/className
Исключение: атрибут "class" соответствует свойству className.
Так как слово "class" является зарезервированным словом в Javascript, то атрибуту "class" соответствует свойство className.
<body>
<script>
document.body.setAttribute('class', 'big red bloom');
alert( document.body.className ); // ^^^
</script>
</body>
Приведенный пример не работает в IE<9, т.к. в старых IE свойства и атрибуты перемешаны.
В реальной жизни рекомендуется всегда использовать свойство, если оно существует. В данном случае лучше работать с document.body.className и забыть про атрибут.
Итак:
- Стандартные свойства синхронизируются с атрибутами.
- .. Но при этом свойство (
href) не всегда равно атрибуту. - .. Некоторые свойства (
value) меняются независимо, т.к. синхронизация односторонняя. - Атрибуту
classсоответствует свойствоclassName.
- Получите
divв переменную. - Получите значение атрибута
"data-widget-name"в переменную. - Выведите его.
Документ:
<body> <div data-widget-name="menu">Выберите жанр</div> <script>/* ... */</script> </body>
Исходный код: tutorial/browser/dom/custom_attribute.html.
-
Для начала, нам нужно получить
div. Здесь нет комментариев, так что сработаетdocument.body.children[0]даже для старых IE.var div = document.body.children[0];
-
Пользовательский атрибут не попадает в свойства DOM-объекта, поэтому используем
методgetAttribute().<body> <div data-widget-name="menu">Выберите жанр</div> <script> var div = document.body.children[0]; var widgetName = div.getAttribute('data-widget-name'); alert( widgetName ); // "menu" </script> </body>
«Особенности» старых IE
Для полноты картины ознакомимся с проблемами версий IE<9.
- Во-первых, версии IE<9 синхронизируют все свойства и атрибуты, а не только стандартные:
document.body.setAttribute('my', 123); alert( document.body.my ); // 123 в IE<9При этом даже тип данных не меняется. Атрибут не становится строкой, как ему положено.
- Во-вторых, в IE8 это исправлено, но в IE<8 (или в IE8 в режиме совместимости с IE7) нет отдельной работы со свойствами и атрибутами. Это одно и то же.
Но это же разные вещи. Поэтому возникают забавные казусы.
Например, названия свойств регистрозависимы, а названия атрибутов - нет. Что будет если два свойства имеют одинаковое имя в разном регистре? Как поведет себя соответствующий атрибут?
document.body.abba = 1; // задаем свойство document.body.ABBA = 5; // задаем свойство, теперь уже прописными буквами // запрашиваем атрибут в *!*смешаном*/!* регистре alert( document.body.getAttribute('AbBa') ); // что должен вернуть браузер?Браузер выходит из ситуации, возвращая первое назначенное свойство(
abba). Также, в IE<9 существует второй параметр дляgetAttribute, который делает его чувствительным к регистру. Подробнее тут:MSDN getAttribute. -
Также есть еще одно следствие смешения свойств с атрибутами в старых IE: изменение атрибута
"class"в IE<9 не меняет класс.
Эта проблема нас никогда не коснется, если мы работаем со свойствомclassName.
Для того, чтобы избежать проблем с IE, используйте атрибуты правильно.
Другими словами, всегда старайтесь использовать свойства, а атрибуты - только там, где это действительно нужно.
А действительно нужны атрибуты лишь в трёх случаях:
- Когда нужно получить пользовательский HTML-атрибут, потому что он не синхронизирован со свойством.
- Когда нужно получить «оригинальное значение» стандартного HTML-атрибута, например,
<INPUT value="...">. Но в IE<8 это не cработает, т.к. там атрибуты смешаны со свойствами, и значение атрибута и свойства будет одинаковое. - Когда нужно получить список всех атрибутов, включая пользовательские. Для этого используется коллекция
attributes.
Итого
Таблица сравнений для атрибутов и свойств:
| Свойства | Атрибуты |
|---|---|
| Любое значение | Строка |
| Названия регистрозависимы | Не чувствительны к регистру |
Не видны в innerHTML |
Видны в innerHTML |
| Стандартные свойства и атрибуты синхронизируются, некоторые - в одну сторону. Пользовательские - не синхронизируются. | |
| В IE<8 и IE8 в режиме совместимости, свойства и атрибуты смешаны воедино. | |
Если вы хотите использовать собственные атрибуты в HTML, то помните, что атрибуты с именем, начинающимся на data- валидны в HTML5.
Современные браузеры поддерживают особый доступ к таким атрибутам через DOM: Свойство dataSet для data-* атрибутов.
В реальной жизни, в 98% случаев используются свойства DOM.
Оставшиеся 2% это:
- Когда нужно получить пользовательский HTML-атрибут, потому что он не синхронизирован со свойством..
- Когда нужно получить именно значение атрибута, потому что в свойстве уже не совсем то (
hrefдля ссылки) или оно изменилось (valueуINPUT).
Комментарии
- Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
- Если ваш комментарий касается задачи -- откройте её в отдельном окне и напишите там.
- Комментарии без смысла, с рекламой или не о статье вообще - удаляются.