renovations
This commit is contained in:
parent
a6431c3f97
commit
dce565963b
115 changed files with 1433 additions and 1563 deletions
|
@ -0,0 +1,20 @@
|
|||
|
||||
|
||||
```html
|
||||
<!--+ run height=100 -->
|
||||
<body>
|
||||
|
||||
<div id="widget" data-widget-name="menu">Выберите жанр</div>
|
||||
|
||||
<script>
|
||||
var div = document.getElementById('widget');
|
||||
|
||||
var widgetName = div.getAttribute('data-widget-name');
|
||||
// или так, кроме IE10-
|
||||
var widgetName = div.dataset.widgetName;
|
||||
|
||||
alert( widgetName ); // "menu"
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYLE HTML>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<div data-widget-name="menu">Выберите жанр</div>
|
||||
|
||||
<script>
|
||||
// ваш код
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,22 @@
|
|||
# Получите пользовательский атрибут
|
||||
|
||||
[importance 5]
|
||||
|
||||
<ol>
|
||||
<li>Получите `div` в переменную.</li>
|
||||
<li>Получите значение атрибута `"data-widget-name"` в переменную.</li>
|
||||
<li>Выведите его.</li>
|
||||
</ol>
|
||||
|
||||
Документ:
|
||||
|
||||
```html
|
||||
<body>
|
||||
|
||||
<div id="widget" data-widget-name="menu">Выберите жанр</div>
|
||||
|
||||
<script>/* ... */</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
[edit src="solution" task/]
|
451
2-ui/1-document/9-attributes-and-custom-properties/article.md
Normal file
451
2-ui/1-document/9-attributes-and-custom-properties/article.md
Normal file
|
@ -0,0 +1,451 @@
|
|||
# Атрибуты и DOM-свойства
|
||||
|
||||
При чтении HTML браузер генерирует DOM-модель. При этом большинство стандартных HTML-атрибутов становятся свойствами соответствующих объектов.
|
||||
|
||||
Например, если тег выглядит как `<body id="page">`, то у объекта будет свойство `body.id = "page"`.
|
||||
|
||||
Но это преобразование -- не один-в-один. Бывают ситуации, когда атрибут имеет одно значение, а свойство -- другое. Бывает и так, что атрибут есть, а свойства с таким названием не создаётся.
|
||||
|
||||
Если коротко -- HTML-атрибуты и DOM-свойства обычно, но не всегда соответствуют друг другу, нужно понимать, что такое свойство и что такое атрибут, чтобы работать с ними правильно.
|
||||
|
||||
[cut]
|
||||
## Свои DOM-свойства
|
||||
|
||||
Ранее мы видели некоторые встроенные свойства DOM-узлов. Но, технически, никто нас ими не ограничивает.
|
||||
|
||||
**Узел DOM -- это объект, поэтому, как и любой объект в JavaScript, он может содержать пользовательские свойства и методы.**
|
||||
|
||||
Например, создадим в `document.body` новое свойство и запишем в него объект:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
document.body.myData = {
|
||||
name: 'Петр',
|
||||
familyName: 'Петрович'
|
||||
};
|
||||
|
||||
alert(document.body.myData.name); // Петр
|
||||
```
|
||||
|
||||
Можно добавить и новую функцию:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
document.body.sayHi = function() {
|
||||
alert(this.nodeName);
|
||||
}
|
||||
|
||||
document.body.sayHi(); // BODY, выполнилась с правильным this
|
||||
```
|
||||
|
||||
Нестандартные свойства и методы видны только в JavaScript и никак не влияют на отображение соответствующего тега.
|
||||
|
||||
Обратим внимание, пользовательские DOM-свойства:
|
||||
|
||||
<ul>
|
||||
<li>Могут иметь любое значение.</li>
|
||||
<li>Названия свойств *чувствительны* к регистру.</li>
|
||||
<li>Работают за счет того, что DOM-узлы являются объектами JavaScript.</li>
|
||||
</ul>
|
||||
|
||||
## Атрибуты
|
||||
|
||||
Элементам DOM, с другой стороны, соответствуют HTML-теги, у которых есть текстовые атрибуты.
|
||||
|
||||
Конечно, здесь речь именно об узлах-элементах, не о текстовых узлах или комментариях.
|
||||
|
||||
Доступ к атрибутам осуществляется при помощи стандартных методов:
|
||||
<ul>
|
||||
<li>`elem.hasAttribute(name)` - проверяет наличие атрибута</li>
|
||||
<li>`elem.getAttribute(name)` - получает значение атрибута</li>
|
||||
<li>`elem.setAttribute(name, value)` - устанавливает атрибут</li>
|
||||
<li>`elem.removeAttribute(name)` - удаляет атрибут</li>
|
||||
</ul>
|
||||
|
||||
Эти методы возвращают именно то значение, которое находится в HTML.
|
||||
|
||||
Также все атрибуты элемента можно получить с помощью свойства `elem.attributes`, которое содержит псевдо-массив объектов типа [Attr](http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-637646024).
|
||||
|
||||
В отличие от свойств, атрибуты:
|
||||
|
||||
<ul>
|
||||
<li>Всегда являются строками.</li>
|
||||
<li>Их имя *нечувствительно* к регистру (ведь это HTML)</li>
|
||||
<li>Видны в `innerHTML` (за исключением старых IE)</li>
|
||||
</ul>
|
||||
|
||||
Рассмотрим отличия между DOM-свойствами и атрибутами на примере HTML-кода:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div id="elem" about="Elephant" class="smiling"></div>
|
||||
</body>
|
||||
```
|
||||
|
||||
Пример ниже устанавливает атрибуты и демонстрирует их особенности.
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<body>
|
||||
<div id="elem" about="Elephant"></div>
|
||||
|
||||
<script>
|
||||
alert( elem.getAttribute('About') ); // (1) 'Elephant', атрибут получен
|
||||
|
||||
elem.setAttribute('Test', 123); // (2) атрибут Test установлен
|
||||
alert( document.body.innerHTML ); // (3) в HTML видны все атрибуты!
|
||||
|
||||
var attrs = elem.attributes; // (4) можно получить коллекцию атрибутов
|
||||
for (var i=0; i<attrs.length; i++) {
|
||||
alert(attrs[i].name + " = " + attrs[i].value);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
При запуске кода выше обратите внимание:
|
||||
<ol>
|
||||
<li>`getAttribute('About')` -- первая буква имени атрибута `About` написана в верхнем регистре, а в HTML -- в нижнем, но это не имеет значения, так как имена нечувствительны к регистру.</li>
|
||||
<li>Мы можем записать в атрибут любое значение, но оно будет превращено в строку. Объекты также будут автоматически преобразованы.</li>
|
||||
<li>После добавления атрибута его можно увидеть в `innerHTML` элемента.</li>
|
||||
<li>Коллекция `attributes` содержит все атрибуты в виде объектов со свойствами `name` и `value`.</li>
|
||||
</ol>
|
||||
|
||||
## Зачем полезны атрибуты?
|
||||
|
||||
Когда браузер читает HTML и создаёт DOM-модель, то он создаёт свойства для всех *стандартных* атрибутов.
|
||||
|
||||
Например, свойства тега `'A'` описаны в спецификации DOM: <a href="http://www.w3.org/TR/REC-DOM-Level-1/level-one-html.html#ID-48250443">HTMLAnchorElement</a>.
|
||||
|
||||
Например, у него есть свойство `"href"`. Кроме того, он имеет `"id"` и другие свойства, общие для всех элементов, которые описаны в спецификации в <a href="http://www.w3.org/TR/REC-DOM-Level-1/level-one-html.html#ID-58190037">HTMLElement</a>.
|
||||
|
||||
В большинстве случаев стандартные свойства DOM синхронизируются с атрибутами. Но не всегда такая синхронизация происходит 1-в-1, поэтому иногда нам нужно значение именно из HTML.
|
||||
|
||||
В этом случае мы берём его через атрибут.
|
||||
|
||||
### Значения могут быть разными: href
|
||||
|
||||
Синхронизация не гарантирует одинакового значения в атрибуте и свойстве.
|
||||
|
||||
Для примера, посмотрим, что произойдет с атрибутом `"href"` при изменении свойства:
|
||||
|
||||
```html
|
||||
<!--+ height=30 run -->
|
||||
<a id="a" href="#"></a>
|
||||
<script>
|
||||
*!*
|
||||
a.href = '/';
|
||||
*/!*
|
||||
|
||||
alert( 'атрибут:' + a.getAttribute('href') ); // '/'
|
||||
alert( 'свойство:' + a.href ); // *!*полный URL*/!*
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
Это происходит потому, что атрибут может быть любым, а свойство `href`, <a href="http://www.w3.org/TR/REC-html40/struct/links.html#adef-href">в соответствии со спецификацией W3C</a>, должно быть полной ссылкой.
|
||||
|
||||
Стало быть, если мы хотим именно то, что в HTML, то нужно обращаться через атрибут.
|
||||
|
||||
[smart header="Есть и другие подобные атрибуты"]
|
||||
|
||||
Кстати, есть и другие атрибуты, которые не копируются в точности. Например, DOM-свойство `input.checked` имеет логическое значение `true/false`, а HTML-атрибут `checked` -- любое строковое, важно лишь его наличие.
|
||||
|
||||
Работа с `checked` через атрибут и свойство:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<input id="input" type="checkbox" checked>
|
||||
|
||||
<script>
|
||||
*!*
|
||||
// работа с checked через атрибут
|
||||
*/!*
|
||||
alert( input.getAttribute('checked') ); // пустая строка
|
||||
input.removeAttribute('checked'); // снять галочку
|
||||
|
||||
*!*
|
||||
// работа с checked через свойство
|
||||
*/!*
|
||||
alert( input.checked ); // false <-- может быть только true/false
|
||||
input.checked = true; // поставить галочку
|
||||
</script>
|
||||
```
|
||||
[/smart]
|
||||
|
||||
### Свойство не всегда обновляет атрибут: value
|
||||
|
||||
Изменение некоторых свойств обновляет атрибут. Но это скорее исключение, чем правило.
|
||||
|
||||
**Обычно синхронизация -- односторонняя: свойство зависит от атрибута.**
|
||||
|
||||
Например, при изменении свойства `input.value` атрибут `input.getAttribute('value')` не меняется:
|
||||
|
||||
```html
|
||||
<!--+ height=30 run -->
|
||||
<body>
|
||||
<input id="input" type="text" value="markup">
|
||||
<script>
|
||||
*!*
|
||||
input.value = 'new'; // поменяли свойство
|
||||
|
||||
alert(input.getAttribute('value')); // 'markup', не изменилось!
|
||||
*/!*
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
То есть, изменение свойства на атрибут не влияет, он остаётся таким же.
|
||||
|
||||
А вот изменение атрибута обновляет свойство:
|
||||
|
||||
```html
|
||||
<!--+ height=30 run -->
|
||||
<body>
|
||||
<input id="input" type="text" value="markup">
|
||||
<script>
|
||||
*!*
|
||||
input.setAttribute('value', 'new'); // поменяли атрибут
|
||||
|
||||
alert( input.value ); // 'new', input.value изменилось!
|
||||
*/!*
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
Эту особенность можно красиво использовать.
|
||||
|
||||
Получается, что атрибут `input.getAttribute('value')` хранит оригинальное (исходное) значение даже после того, как пользователь заполнил поле и свойство изменилось.
|
||||
|
||||
Например, можно взять изначальное значение из атрибута и сравнить со свойством, чтобы узнать, изменилось ли значение. А при необходимости и перезаписать свойство атрибутом, отменив изменения.
|
||||
|
||||
### Атрибут class -- className
|
||||
|
||||
Атрибуту `"class"` соответствует свойство `className`.
|
||||
|
||||
Так как слово `"class"` является зарезервированным словом в Javascript, то при проектировании DOM решили, что соответствующее свойство будет называться `className`.
|
||||
|
||||
Например:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<body class="main page">
|
||||
<script>
|
||||
// прочитать класс элемента
|
||||
alert( document.body.className ); // main page
|
||||
|
||||
// поменять класс элемента
|
||||
document.body.className="class1 class2";
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
Кстати, есть и другие атрибуты, которые называются иначе, чем свойство. Например, атрибуту `for` (`<label for="...">`) соответствует свойство с названием `htmlFor`.
|
||||
|
||||
### Атрибут class -- объект classList
|
||||
|
||||
Атрибут `class` -- уникален. Ему соответствует аж целых два свойства!
|
||||
|
||||
Работать с классами как со строкой неудобно. Поэтому, кроме `className`, в современных браузерах есть свойство `classList`.
|
||||
|
||||
**Свойство `classList` -- это объект для работы с классами.**
|
||||
|
||||
Оно поддерживается в IE начиная с IE10, но его можно эмулировать в IE8+, подключив мини-библиотеку [classList.js](https://github.com/eligrey/classList.js).
|
||||
|
||||
Методы `classList`:
|
||||
<ul>
|
||||
<li>`elem.classList.contains("class")` -- возвращает `true/false`, в зависимости от того, есть ли у элемента класс `class`.</li>
|
||||
<li>`elem.classList.add/remove("class")` -- добавляет/удаляет класс `class`</li>
|
||||
<li>`elem.classList.toggle("class")` -- если класса `class` нет, добавляет его, если есть -- удаляет.</li>
|
||||
</ul>
|
||||
|
||||
Кроме того, можно перебрать классы через `for`, так как `classList` -- это псевдо-массив.
|
||||
|
||||
Например:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<body class="main page">
|
||||
<script>
|
||||
var classList = document.body.classList;
|
||||
|
||||
classList.remove('page'); // удалить класс
|
||||
classList.add('post'); // добавить класс
|
||||
|
||||
for(var i=0; i<classList.length; i++) { // перечислить классы
|
||||
alert(classList[i]); // main, затем post
|
||||
}
|
||||
|
||||
alert( classList.contains('post') ); // проверить наличие класса
|
||||
|
||||
alert( document.body.className ); // main post, тоже работает
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
## Нестандартные атрибуты
|
||||
|
||||
У каждого элемента есть некоторый набор стандартных свойств, например для `<a>` это будут `href`, `name`, `title`, а для `<img>` это будут `src`, `alt`, и так далее.
|
||||
|
||||
Точный набор свойств описан в стандарте, обычно мы более-менее представляем, если пользуемся HTML, какие свойства могут быть, а какие -- нет.
|
||||
|
||||
Для нестандартных атрибутов DOM-свойство не создаётся.
|
||||
|
||||
Например:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<div id="elem" href="http://ya.ru" about="Elephant"></div>
|
||||
|
||||
<script>
|
||||
alert( elem.id ); // elem
|
||||
*!*
|
||||
alert( elem.about ); // undefined
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
Стандартным свойство является, лишь если оно описано в стандарте именно для этого элемента.
|
||||
|
||||
То есть, если назначить элементу `<img>` атрибут `href`, то свойство `img.href` от этого не появится. Как, впрочем, и если назначить ссылке `<a>` атрибут `alt`:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<img id="img" href="test">
|
||||
<a id="link" alt="test"></a>
|
||||
|
||||
<script>
|
||||
alert( img.href ); // undefined
|
||||
alert( link.alt ); // undefined
|
||||
</script>
|
||||
```
|
||||
|
||||
## Свойство dataSet, data-атрибуты
|
||||
|
||||
Нестандартные атрибуты никак не влияют на внешний вид элемента, но с их помощью можно привязать к элементу данные, которые будут доступны в JavaScript.
|
||||
|
||||
Как правило, это делается при помощи атрибутов с названиями, начинающимися на `data-`, например:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<div id="elem" *!*data-about*/!*="Elephant" *!*data-user-location*/!*="street">
|
||||
По улице прошёлся слон. Весьма красив и толст был он.
|
||||
</div>
|
||||
<script>
|
||||
alert( elem.getAttribute('data-about') ); // Elephant
|
||||
alert( elem.getAttribute('data-user-location') ); // street
|
||||
</script>
|
||||
```
|
||||
|
||||
[Стандарт HTML5](http://www.w3.org/TR/2010/WD-html5-20101019/elements.html#embedding-custom-non-visible-data-with-the-data-attributes) специально разрешает атрибуты `data-*` и резервирует их для пользовательских данных.
|
||||
|
||||
При этом во всех браузерах, кроме IE10-, к таким атрибутам можно обратиться не только как к атрибутам, но и как к свойствам, при помощи специального свойства `dataset`:
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<div id="elem" data-about="Elephant" data-user-location="street">
|
||||
По улице прошёлся слон. Весьма красив и толст был он.
|
||||
</div>
|
||||
<script>
|
||||
*!*
|
||||
alert( elem.dataset.about ); // Elephant
|
||||
alert( elem.dataset.userLocation ); // street
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
Обратим внимание -- название `data-user-location` трансформировалось в `dataset.userLocation`. Дефис превращается в большую букву.
|
||||
|
||||
## "Особенности" IE8-
|
||||
|
||||
Если вам нужна поддержка этих версий IE -- ознакомьтесь с их проблемами. Ничего критичного, но они, всё же, есть.
|
||||
|
||||
<ol>
|
||||
<li>Во-первых, версии IE8- синхронизируют <u>все</u> свойства и атрибуты, а не только стандартные:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
document.body.setAttribute('my', 123);
|
||||
|
||||
alert( document.body.my ); // 123 в IE<9
|
||||
```
|
||||
|
||||
При этом даже тип данных не меняется. Атрибут не становится строкой, как ему положено.
|
||||
</li>
|
||||
<li>Во-вторых, в IE7- (или в IE8 в режиме совместимости с IE7) свойства и атрибуты -- одно и то же.
|
||||
|
||||
Поэтому возникают забавные казусы.
|
||||
|
||||
Например, названия свойств регистрозависимы, а названия атрибутов -- нет. Что будет если два свойства имеют одинаковое имя в разном регистре? Как поведет себя соответствующий атрибут?
|
||||
|
||||
```js
|
||||
//+ run
|
||||
document.body.abba = 1; // задаем свойство
|
||||
document.body.ABBA = 5; // задаем свойство, теперь уже прописными буквами
|
||||
|
||||
// запрашиваем атрибут в *!*смешаном*/!* регистре
|
||||
alert( document.body.getAttribute('AbBa') ); // что должен вернуть браузер?
|
||||
```
|
||||
|
||||
Браузер выходит из ситуации, возвращая первое назначенное свойство(`abba`). Также, в IE8- существует второй параметр для `getAttribute`, который делает его чувствительным к регистру. Подробнее тут:<a href="http://msdn.microsoft.com/en-us/library/ms536429(v=vs.85).aspx">MSDN getAttribute</a>.
|
||||
</li>
|
||||
<li>Ещё одна некорректность IE8-: для изменения класса нужно использовать именно свойство `className/classList`, вызов `setAttribute('class', ...)` не сработает.</li>
|
||||
</ol>
|
||||
|
||||
Вывод из этого довольно прост -- чтобы не иметь проблем в IE8, нужно использовать всегда только свойства, кроме тех ситуаций, когда нужны именно атрибуты. Впрочем, это в любом случае хорошая практика.
|
||||
|
||||
А для IE7- тонких различий между свойствами и атрибутами, о которых мы говорили выше, нет. Впрочем, надеюсь, вам эти версии IE поддерживать не придётся.
|
||||
|
||||
|
||||
## Итого
|
||||
|
||||
<ul>
|
||||
<li>Атрибуты -- это то, что написано в HTML.</li>
|
||||
<li>Свойство -- это то, что находится в свойстве DOM-объекта.</li>
|
||||
</ul>
|
||||
|
||||
Таблица сравнений для атрибутов и свойств:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Свойства</th>
|
||||
<th>Атрибуты</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Любое значение</td>
|
||||
<td>Строка</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Названия регистрозависимы</td>
|
||||
<td>Не чувствительны к регистру</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Не видны в `innerHTML`</td>
|
||||
<td>Видны в `innerHTML`</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Синхронизация между атрибутами и свойствами:
|
||||
<ul>
|
||||
<li>Стандартные свойства и атрибуты синхронизируются: установка атрибута автоматически ставит свойство DOM. Некоторые свойства синхронизируются в обе стороны.</li>
|
||||
<li>Бывает так, что свойство не совсем соответствует атрибуту. Например, "логические" свойства вроде `checked`, `selected` всегда имеют значение `true/false`, а в атрибут можно записать произвольную строку.Выше мы видели другие примеры на эту тему, например `href`.</li>
|
||||
</ul>
|
||||
|
||||
Нестандартные атрибуты:
|
||||
<ul>
|
||||
<li>Нестандартный атрибут (если забыть глюки старых IE) никогда не попадёт в свойство, так что для кросс-браузерного доступа к нему нужно обязательно использовать `getAttribute`.</li>
|
||||
<li>Атрибуты, название которых начинается с `data-`, можно прочитать через `dataset`. Эта возможность не поддерживается IE10-.</li>
|
||||
</ul>
|
||||
|
||||
Для того, чтобы избежать проблем со старыми IE, а также для более короткого и понятного кода старайтесь везде использовать свойства, а атрибуты -- только там, где это *действительно* нужно.
|
||||
|
||||
А *действительно* нужны атрибуты очень редко - лишь в следующих трёх случаях:
|
||||
<ol>
|
||||
<li>Когда нужно кросс-браузерно получить нестандартный HTML-атрибут.</li>
|
||||
<li>Когда нужно получить "оригинальное значение" стандартного HTML-атрибута, например, `<input value="...">`.
|
||||
</li>
|
||||
<li>Когда нужно получить список всех атрибутов, включая пользовательские. Для этого используется коллекция `attributes`.</li>
|
||||
</ol>
|
||||
|
||||
Если вы хотите использовать собственные атрибуты в HTML, то помните, что атрибуты с именем, начинающимся на `data-` валидны в HTML5 и современные браузеры поддерживают доступ к ним через свойство `dataset`.
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue