DOM, часть 4 из 4. Атрибуты и свойства

13.05.2022

Теги: DOMFrontendHTMLJavaScriptWeb-разработкаДокументТеория

HTML-атрибуты

При написании HTML-кода мы можем задавать атрибуты для атрибуты HTML-элементов. Атрибуты могут быть стандартными и нестандартными, причем стандартный атрибут для одного тега может быть нестандартным для другого. Например, атрибут type является стандартным для элемента <input>, но не является стандартным для <body>.

Все атрибуты доступны с помощью следующих методов:

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

Эти методы работают именно с тем, что мы написали в HTML-коде. Имена HTML-атрибутов регистронезависимы, а значения всегда являются строками. Получить все атрибуты элемента можно с помощью свойства elem.attributes — это коллекция объектов, которая принадлежит ко встроенному классу Attr со свойствами name и value.

<body id="content" something="some value">
    <script>
        // имя атрибута не завистит от регистра
        if (document.body.hasAttribute('ID')) {
            alert(document.body.getAttribute('id')); // content
        }
        // пример чтения нестандартного атрибута
        alert(document.body.getAttribute('something')); // some value
        // добавим еще один атрибут для body
        document.body.setAttribute('style', 'background:yellow');
        // покажем весь список атрибутов body
        for (let attr of document.body.attributes) {
            alert(`${attr.name} = ${attr.value}`);
        }
    </script>
</body>

DOM-свойства

Когда браузер загружает страницу, он «читает» HTML-код и генерирует из него DOM-объекты. Для узлов-элементов большинство стандартных HTML-атрибутов автоматически становятся свойствами DOM-объектов. Например, для тега <body id="page"> у DOM-объекта будет такое свойство body.id="page".

DOM-узлы — это обычные объекты JavaScript, мы можем их изменять:

document.body.something = {
    name: 'some name',
    value: 'some value'
};
document.body.sayTagName = function() {
    alert(this.tagName);
};

Синхронизация атрибутов и свойств

Когда стандартный атрибут изменяется, соответствующее свойство автоматически обновляется. Это работает и в обратную сторону (за некоторыми исключениями).

<input>

<script>
    let input = document.querySelector('input');

    // атрибут => свойство
    input.setAttribute('id', 'email');
    alert(input.id); // email (обновлено)

    // свойство => атрибут
    input.id = 'phone';
    alert(input.getAttribute('id')); // phone (обновлено)
</script>
<input>

<script>
  let input = document.querySelector('input');

  // атрибут => свойство
  input.setAttribute('value', 'значение');
  alert(input.value); // значение

  // свойство => атрибут
  input.value = 'новое значение';
  alert(input.getAttribute('value')); // значение (не обновилось!)
</script>

В примере выше:

  • Изменение атрибута value обновило свойство
  • Но изменение свойства не повлияло на атрибут

Иногда эта «особенность» может пригодиться, потому что действия пользователя могут приводить к изменениям value, и если после этого мы захотим восстановить «оригинальное» значение из HTML-кода, оно будет в атрибуте.

DOM-свойства типизированы

DOM-свойства не всегда являются строками. Например, свойство input.checked (для чекбоксов) имеет логический тип.

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

<script>
    alert(input.getAttribute('checked')); // значение атрибута — пустая строка
    alert(input.checked); // значение свойства — логическое true
</script>

Есть и другие примеры. Атрибут style — строка, но свойство style является объектом:

<div id="div" style="color:red;font-size:120%">Hello</div>

<script>
    // атрибут style — строка
    alert(div.getAttribute('style')); // color:red;font-size:120%

    // свойство style — объект
    alert(div.style); // [object CSSStyleDeclaration]
    alert(div.style.color); // red
</script>

Хотя большинство свойств, всё же, строки. При этом некоторые из них, хоть и строки, могут отличаться от атрибутов. Например, DOM-свойство href всегда содержит полный URL, даже если атрибут содержит относительный URL или просто #hash.

<a id="link" href="#hello">Ссылка</a>

<script>
    // атрибут href
    alert(link.getAttribute('href')); // #hello

    // свойство href
    alert(link.href); // полный URL в виде http://site.com/some/path#hello
</script>

Если же нужно значение href или любого другого атрибута в точности, как оно записано в HTML-коде, можно воспользоваться getAttribute.

Нестандартные атрибуты, dataset

При написании HTML-кода мы используем много стандартных атрибутов. Но что насчёт нестандартных, пользовательских? Они тоже часто нужны — например, для передачи пользовательских данных из HTML-кода в JavaScript, или чтобы «помечать» HTML-элементы для JavaScript.

<!-- пометить div, чтобы показать здесь name -->
<div show-info="name"></div>
<!-- пометить div, чтобы показать здесь age -->
<div show-info="age"></div>

<script>
    // код находит элемент с пометкой и показывает информацию
    let user = {
        name: 'Сергей',
        age: 25
    };

    for (let div of document.querySelectorAll('[show-info]')) {
        // вставить соответствующую информацию в поле
        let field = div.getAttribute('show-info');
        div.textContent = user[field];
    }
</script>

Но с пользовательскими атрибутами могут возникнуть проблемы. Что если мы используем нестандартный атрибут для наших целей, а позже он появится в стандарте и будет выполнять какую-то функцию? Чтобы избежать конфликтов, были придуманы data-атрибуты, доступ к значениям которых из js-кода возможен через dataset.

<div id="user" data-name="Сергей" data-age="25"></div>

<script>
    alert(user.dataset.name); // Сергей
    alert(user.dataset.age); // 25
</script>

className и classList

Изменение класса является одним из наиболее часто используемых действий в скриптах. Атрибут class в HTML-коде соответствует атрибуту className элемента.

<body class="main page">
    <script>
        alert(document.body.className); // main page
    </script>
</body>

Если мы присваиваем что-то elem.className, то это заменяет всю строку с классами. Иногда это то, что нам нужно, но часто мы хотим добавить или удалить один класс. Для этого есть другое свойство — elem.classList.

<body class="main page">
    <script>
        // добавление css-класса для body
        document.body.classList.add('article');
        alert(document.body.className); // main page article
    </script>
</body>

Так что мы можем работать как со строкой полного класса, используя className, так и с отдельными классами, используя classList.

  • elem.classList.add(class) — добавить css-класс
  • elem.classList.remove(class) — удалить css-класс
  • elem.classList.toggle(class) — добавить css-класс, если его нет, иначе — удалить
  • elem.classList.contains(class) — проверка наличия css-класса, возвращает true или false

Кроме того, classList является перебираемым, поэтому можно перечислить все классы при помощи цикла:

<body class="main page">
    <script>
        for (let name of document.body.classList) {
            alert(name); // main, page
        }
    </script>
</body>

Поиск: DOM • Frontend • HTML • JavaScript • Web-разработка • Документ • Теория

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.