Атрибуты и свойства
Когда браузер загружает страницу, он «читает» (также говорят: «парсит») HTML и генерирует из него DOM-объекты. Для узлов-элементов большинство стандартных HTML-атрибутов автоматически становятся свойствами DOM-объектов.
Например, для такого тега <body > у DOM-объекта будет такое свойство body.id="page" .
Но преобразование атрибута в свойство происходит не один-в-один! В этой главе мы уделим внимание различию этих двух понятий, чтобы посмотреть, как работать с ними, когда они одинаковые и когда разные.
DOM-свойства
Ранее мы уже видели встроенные DOM-свойства. Их много. Но технически нас никто не ограничивает, и если этого мало – мы можем добавить своё собственное свойство.
Основы JavaScript: управление DOM элементами (часть 1)
Объектная модель документа или DOM определяет логическую структуру HTML документа и в основном представляет собой интерфейс веб-страниц. С помощь таких языков программирования, как JavaScript, мы можем получить доступ к DOM и управлять веб-сайтами для создания интерактивности.
Что такое DOM?
В своей основе сайт должен содержать HTML-документ — index.html . Используя браузер, мы просматриваем сайт, который формирует наш(и) HTML-файл(ы) и любые CSS файлы, которые добавляют стили и правила разметки.
Браузер также создает представление этого документа, известное как объектная модель документа (DOM). Именно с помощью DOM JavaScript способен получать доступ и изменять содержимое элементов на сайте.
Для просмотра DOM в браузере необходимо кликнуть в любом месте на странице и выбрать «Исследовать элемент». После этого откроется вкладка инструментов разработчика, вот так:
DOM отображается во вкладке Elements. Вы также можете увидеть его, выбрав вкладку Console и напечатав “document”.
Объект Document
Объект document — это встроенный объект, содержащий много свойств и методов.
Мы получаем доступ и управляем этим объектом с помощью JavaScript. И, управляя DOM, мы можем сделать наши страницы интерактивными, ведь мы больше не ограничены обычным построением статических сайтов со стилизованным HTML содержимым.
Теперь мы можем создавать приложения, которые обновляют данные без необходимости перезагрузки страницы; мы можем дать пользователям возможность кастомизировать разметку страницу; мы можем создавать элементы, которые можно двигать по экрану, браузерные игры, часы, таймеры и сложные анимации. Работа с DOM открывает множество возможностей!
Попробуем сделать нашу первую манипуляцию DOM…
Зайдите на сайт www.google.com и откройте панель разработчика. Затем выберете вкладку Console и напечатайте следующее:
Нажмите enter и вы увидите, что фон поменялся на оранжевый.
Конечно, вы не изменили исходный код Google (!), но вы изменили то, как содержимое отображается в вашем браузере, управляя объектом document .
Document — это объект, body — это свойство, которое мы решили изменить, обратившись к атрибуту style и изменив его свойство backgroundColor на оранжевый.
Обратите внимание на регистр backgroundColor в JavaScript, вместо background-color , используемого в CSS. Любое свойство, прописанное через дефис в JavaScript, будет писаться в CamelCase.
Вы можете увидеть изменения DOM в элементе body во вкладке Elements или написав document.body в консоль.
Так как мы работаем напрямую с DOM в браузере, в действительности мы не меняем исходный код. Если вы обновите страницу, то все изменения исчезнут.
Дерево DOM и узлы
Ввиду разметки DOM, его часто называют деревом DOM.
Дерево состоит из объектов, называемых узлами. Существует множество типов узлов, но чаще всего вы будете работать с узлами элементов (HTML-элементами), текстовыми узлами (любое текстовое содержимое), а также с комментариями (закомментированный код). Объект document находится в собственном узле, который располагается в корне.
Работая с DOM узлами, мы также обращаемся к родительским, дочерним или соседним элементам (элементы, имеющие общего родителя), в зависимости от их связи с другими узлами.
В коде выше узел html элемента является родительским узлом, а head и body элементы являются соседними. Body содержит три дочерних узла (которые являются соседними по отношению друг к другу — как в семейном дереве). Мы рассмотрим это подробнее позже.
Как определить тип узла
Так как каждый узел в документе имеет тип, мы можем получить к нему доступ, используя свойство nodeType . Полный список типов узлов вы можете просмотреть здесь.
Посмотрим на пару примеров типов из нашего предыдущего примера:
<html> , <title> , <body> и <h1> относятся к типу ELEMENT_NODE со значением 1 .
Текст This is a text node. , расположенный внутри body, не являющегося частью элемента, это TEXT_NODE со значением 3 .
Наш комментарий <!— This is a comment node —> — это COMMENT_NODE со значением 8 .
Как проверить тип узла?
Перейдите во вкладку Elements в панели разработчика и кликните на любую строку. Вы увидите значение == $0 рядом. Теперь, если вы перейдете во вкладку Console и введете $0 , отобразится выбранный вами ранее элемент. Для проверки типа узла наберите:
Будет отображено числовое значение выбранного узла. Например, если вы выбрали h1 , вы увидите 1 . Для текста значение будет 3 , а для комментария 8 .
И когда вы узнаете, где располагается узел в DOM, вам не нужно будет выбирать его вручную, вы можете обратиться к нему напрямую:
Также вы можете использовать nodeValue для получения значения текста или комментария и nodeName для отображения названия тэга, содержащего элемент.
Заключение
В данной статье мы рассмотрели понятие DOM дерева, элементов DOM, объект document и узлы. В следующей части мы рассмотрим, как получать доступ к DOM элементам.
Введение в DOM.
DOM (Document Object Model) — внутренние объекты и функции браузера, хранящие состояние страницы.JS через это API имеет полный доступ ко всему тому, что есть в HTML и CSS.
Корень
Корнем дерева элементов DOM является объект document
Поиск элементов
Что бы найти элемент, нужно обратится к методу document , или любого другого элемента, в котором нужно что-то найти:
Создание элементов DOM
Ссылка на родительский элемент находится в свойстве parentElement :
Свойства объектов или наборов объектов элементов в DOM
- value — свойство а не функция для значения поля ввода.
- attributes — объект attributes с атрибутами html-тега. Также есть 4 функции для работы с атрибутами:
- hasAttribute — проверка на наличие атрибута
- getAttribute — чтение
- setAttribute — запись
- removeAttribute — удаление
- style — объект стиля элемента
- innerHTML — строка вложенного HTML в элементе.
- innerText — строка вложенного текста в элементе.
Нюансы
Элемент в дереве
может встречаться только один раз. Вы не можете вставить элемент дважды в дерево. Если вы хотите создать копию элемента в вашем DOM-дереве, используйте cloneNode .
HTML/CSS
Всё, что вы видели в HTML/CSS может быть установлено как свойства объекта-узла DOM и тут же будет отображено в браузере
children и childNodes
- Узлами ( Node ) может быть любой текст в HTML, в том числе обычный текст и тот или иной тэг. Дочерние элементы каждого элемента находятся в псевдомассиве
HTMLCollection это список узлов. Отдельный узел может быть доступен по порядковому номеру или имени узла и атрибута. Коллекции в HTML DOM являются живыми. Коллекции обновляются при изменении документа. Коллекция HTML всегда находится в DOM, в то время как NodeList является более универсальной конструкцией, которая может или не может быть в DOM.
NodeList является коллекцией узлов. NodeList обеспечивает уровень абстракции над коллекцией узлов, не определяя и не ограничивая как это коллекция используется. Объекты NodeList являются “живыми” или статическим, в зависимости от способа используемого для их получения.
События
Каждый элемент DOM содержит множество свойств on. , в которые вы можете занести тот или иной обработчик события:
Так же можно добавлять обработчики используя метод элемента addEventListener :
Как было уже отмечено, DOM — это дерево из элементов верстки страницы, каждый из которых представлен объектом Javascript. Кроме данных, в объектах бывают те или иные действия, которые объект может осуществлять — методы.
Установка обработчиков
Для обработки событий вы можете назначить функцию-обработчик события путем присвоения ключа объекта:
Так же вы можете добавить несколько обработчиков на одно и тоже событие в одном и том же элементе, используя метод addEventListener :
Данные, передаваемые в обработчик
В обработчик события передается два аргумента:
- this , который ссылается на элемент, на который "навешен" обработчик. Используя this вы можете узнать любую информацию о элементе и навешивать один и тот же обработчик на несколько элементов, если вам нужно схожее поведение на многих элементах.
- event , объект-событие, переданный первым аргументом в функцию-обработчик (вы можете назвать его как угодно). С помощью этого объекта можно узнать информацию о событии (нажатые кнопки мыши и клавиатуры, положение курсора мыши, и т. п.), а так же управлять обработкой потока событий:
- event.stopPropagation() останавливает всплытие событий (например, click всплывает от вложенных элементов к обрамляющим). Обрамляющие элементы не узнают о событии.
- event.stopImmediatePropagation() аналогичен предыдущему, однако не будут запущены даже следующие обработчики на этом элементе
- event.preventDefault() запрещает браузеру запускать обработчики по умолчанию (отправку данных для submit формы, переход по клику на ссылку <a> и т. п.)
addEventListener
Первый аргумент метода addEventListener — это тип события ( строка ), например: "mouseover" "mouseout" "input" "change" . Второй аргумент — ссылка на функцию ( обработчика события ) Третий аргумент — логическое значение — будучи установленным в true, позволяет перехватить событие на фазу погружения ( capturing )
removeEventListener
Прослушивателей событий нужно удалять, поскольку они не убираются автоматически при удалении элемента
При удалении нужно передавать точно такие же аргументы, какие были переданы методу addEventListener при создании прослушивателя
Не правильно
✅ Правильно
Всплытие событий
Всплытие
Принцип всплытия очень простой. Когда на элементе происходит событие, обработчики сначала срабатывают на нём, потом на его родителе, затем выше и так далее, вверх по цепочке предков.
Например, есть 3 вложенных элемента FORM > DIV > P с обработчиком на каждом
Клик по внутреннему <p> вызовет обработчик onclick:
- Сначала на самом <p> .
- Потом на внешнем <div> .
- Затем на внешнем <form> . И так далее вверх по цепочке до самого document .
Поэтому если кликнуть на <p> , то мы увидим три оповещения: p → div → form .
Этот процесс называется «всплытием», потому что события «всплывают» от внутреннего элемента вверх через родителей подобно тому, как всплывает пузырёк воздуха в воде.
event.target
Всегда можно узнать, на каком конкретно элементе произошло событие.
Самый глубокий элемент, который вызывает событие, называется целевым элементом, и он доступен через event.target .
ℹ️ Отличия от this = evet.currentTarget
event.target – это «целевой» элемент, на котором произошло событие, в процессе всплытия он неизменен.
this – это «текущий» элемент, до которого дошло всплытие, на нём сейчас выполняется обработчик.
Например, если стоит только один обработчик form.onclick , то он «поймает» все клики внутри формы. Где бы ни был клик внутри – он всплывёт до элемента <form> , на котором сработает обработчик. При этом внутри обработчика form.onclick :
- this = event.currentTarget всегда будет элемент <form> , так как обработчик сработал на ней.
- event.target будет содержать ссылку на конкретный элемент внутри формы, на котором произошёл клик.
Прекращение всплытия
Всплытие идёт с «целевого» элемента прямо наверх. По умолчанию событие будет всплывать до элемента <html> , а затем до объекта document , а иногда даже до window , вызывая все обработчики на своём пути. Но любой промежуточный обработчик может решить, что событие полностью обработано, и остановить всплытие. Для этого нужно вызвать метод event.stopPropagation() .
Погружение
Существует ещё одна фаза из жизненного цикла события – «погружение» (иногда её называют «перехват»). Она очень редко используется в реальном коде, однако тоже может быть полезной.
Стандарт DOM Events описывает 3 фазы прохода события:
- Фаза погружения (capturing phase) – событие сначала идёт сверху вниз.
- Фаза цели (target phase) – событие достигло целевого(исходного) элемента.
- Фаза всплытия (bubbling stage) – событие начинает всплывать.
Изображение ниже из спецификации демонстрирует, как это работает при клике по ячейке <td> , расположенной внутри таблицы:
При клике на <td> событие путешествует по цепочке родителей сначала вниз к элементу (погружается), затем оно достигает целевой элемент (фаза цели), а потом идёт наверх (всплытие), вызывая по пути обработчики. Ранее мы говорили только о всплытии, потому что другие стадии, как правило, не используются и проходят незаметно для нас.
Обработчики, добавленные через on <event> -свойство или через HTML-атрибуты, или через addEventListener(event, handler) с двумя аргументами, ничего не знают о фазе погружения, а работают только на 2-ой и 3-ей фазах. Чтобы поймать событие на стадии погружения, нужно использовать третий аргумент capture вот так:
Существуют два варианта значений опции capture:
- Если аргумент false (по умолчанию), то событие будет поймано при всплытии.
- Если аргумент true , то событие будет перехвачено при погружении.
Обратите внимание, что хоть и формально существует 3 фазы, 2-ую фазу («фазу цели»: событие достигло элемента) нельзя обработать отдельно, при её достижении вызываются все обработчики: и на всплытие, и на погружение.
Давайте посмотрим и всплытие и погружение в действии:
Пример 2
Здесь обработчики навешиваются на каждый элемент внутри элемента с id ex2 , чтобы увидеть в каком порядке они вызываются по мере прохода события.
Если вы кликните по <p> , то последовательность следующая:
FORM → DIV → p (фаза погружения, первый обработчик) P (фаза цели, срабатывают обработчики, установленные и на погружение и на всплытие, так что выведется два раза) P→ DIV → FORM (фаза всплытия, второй обработчик) Существует свойство event.eventPhase , содержащее номер фазы, на которой событие было поймано. Но оно используется редко, мы обычно и так знаем об этом в обработчике.
❗️ Важно
Чтобы убрать обработчик removeEventListener , нужна та же фаза Если мы добавили обработчик вот так addEventListener(. true) , то мы должны передать то же значение аргумента capture в removeEventListener(. true) , когда снимаем обработчик.
Работа с атрибутами и свойствами элементов в JavaScript
Атрибуты — это HTML-сущности, с помощью которых мы можем добавить определённые данные к элементам в HTML-коде.
Когда браузер запрашивает некоторую страницу, он получает её исходный HTML-код. После этого он парсит этот код и строит на его основании DOM. Во время этого процесса HTML-атрибуты элементов переводятся в соответствующие DOM-свойства .
Например, браузер, при чтении следующей HTML-строчки кода, создаст для этого элемента следующие DOM-свойства: id , className , src и alt .
Обращение к этим свойствам в коде JavaScript выполняется как к свойствам объекта. Объектом здесь выступает узел (элемент) DOM.
Пример, в котором получим значения DOM-свойств для элемента, приведённого выше, и выведем их значения в консоль:
Некоторые названия DOM-свойств не соответствуют именам атрибутов. Одним из таких является атрибут class . Данному атрибуту соответствует DOM-свойство className . Данное отличие связано с тем, что class является ключевым словом в JavaScript, оно зарезервировано и не может использоваться. Из-за этого разработчики стандарта решили использовать для соответствия какое-то другое название, в качестве которого было выбрано className .
Ещё один нюанс связан с тем, что перевод HTML-атрибутов, заданных в исходном коде документа, в DOM-свойства не всегда осуществляется один к одному.
Если элемент имеет нестандартный HTML-атрибут, то свойство, соответствующее ему в DOM, не создаётся .
Другое отличие связано с тем, что значения определённых HTML-атрибутов и соответствующих им DOM-свойств могут быть различными. Т.е. атрибут может иметь одно значение, а DOM-свойство, созданное на его основе – другое .
Одним из таких атрибутов является checked .
Значение HTML-атрибута checked в данном случае – это пустая строка. Но, свойство, соответствующее данному атрибуту в DOM, будет иметь значение true . Т.к. по правилам стандарта для установления true достаточно лишь упоминание этого атрибута в HTML-коде и при этом не важно какое он будет иметь значение.
При этом даже если мы в HTML-коде не укажем атрибут checked для элемента input с типом checkbox , то для него в DOM всё равно будет создано свойство checked , но оно будет равно false .
Кроме этого, JavaScript позволяет также работать с атрибутами. Для этого в DOM API имеются специальные методы. Но их желательно использовать только тогда, когда вам действительно нужно работать с данными именно так.
При этом нужно знать, что, когда мы изменяем DOM-свойство элемента, изменяется и соответствующий ему атрибут, и наоборот. Но это процесс в браузерах выполнятся не всегда один к одному.
Основные отличия между DOM-свойствами и атрибутами:
- значение атрибута – это всегда строка, а значение DOM-свойства – определённый тип данных (не обязательно строка);
- имя атрибута – регистронезависимо, а DOM-свойства — регистрозависимо. Т.е. в HTML-коде мы можем, например, HTML-атрибут id написать, как Id , ID и т.д. То же касается и имени атрибута, которые мы указываем в специальных методах JavaScript для работы с ним. Но к соответствующему DOM-свойству мы можем обратиться только по id и никак по-другому.
Работа с DOM-свойствами элемента
Работа со свойствами элементов в JavaScript как уже было отмечено выше осуществляется как со свойствами объектов.
Но для того, чтобы обратиться к свойству некоторого элемента, его необходимо сначала получить. Получить DOM-элемент в JavaScript можно, например, с помощью универсального метода querySelector , а коллекцию DOM элементов, например, посредством querySelectorAll .
В качестве первого примера рассмотрим следующий HTML-элемент:
На базе него разберём как осуществляется получение DOM-свойств, их изменение и добавление новых.
Чтение значений DOM-свойств:
Изменение значений DOM-свойств:
Пример, в котором выведем в консоль все значения классов, которые есть у элементов p на странице:
Пример, в котором установим всем элементам с классом content свойство lang со значением «ru»:
Атрибуты элементов и методы для работы с ними
Атрибуты изначально задаются в HTML-коде. Они хоть и связаны, некоторым образом, со свойствами, но это не одно и тоже. В большинстве случаев следует работать именно со свойствами, а к атрибутам обращаться только тогда, когда это действительно нужно.
Значения атрибутов, в отличие от DOM-свойств, как это уже было отмечено выше всегда является строкой.
В JavaScript для выполнения операций, связанных с атрибутами, имеется четыре метода:
- .hasAttribute(‘имя_атрибута’) – проверяет наличие указанного атрибута у элемента. Если проверяемый атрибут есть у элемента, то данный метод возвращает true , в противном случае — false .
- .getAttribute(‘имя_атрибута’) – получает значение атрибута. Если указанного атрибута нет у элемента, то данный метод возвращает пустую строку («») или null .
- .setAttribute(‘имя_атрибута’, ‘значение_атрибута’) – устанавливает указанный атрибут с указанным значением элементу. Если указанный атрибут есть у элемента, то данный метод тогда просто изменит ему значение.
- .removeAttribute(‘имя_атрибута’) — удаляет указанный атрибут у элемента.
Очень интересный пример с атрибутом value .
Пример с атрибутом value
Получим значение атрибута value и DOM-свойства value :
Из этого примера видно, что, при измении атрибута value , браузер автоматически изменяет в соответствии с ним DOM-свойство value .
Теперь давайте проделаем действия, наоборот, а именно изменим значение DOM-свойства и проверим изменится ли значение атрибута:
Из этого примера видно, что не всегда изменение DOM-свойства приводит к соответствующему изменению атрибута. Т.е. в этом случае изменение DOM-свойства value не изменяет соответствующий ему атрибут .
Тоже самое произойдёт, когда пользователь будет вводить текст в это поле. В DOM-свойстве value будет находится действительное значение, а в соответствующем ему атрибуте изначальное или то, которое мы установили, например, с помощью метода setAttribute .
Этот пример показывает, что более корректно работать всегда с DOM-свойствами, а обращаться к атрибуту нужно только тогда, когда это действительно необходимо.
Даже в случае, когда вам нужно получить начальное значение value , которое мы установили в HTML, можно воспользоваться свойством. Свойство, содержащее начальное значение атрибута value называется defaultValue .
Ещё один очень интересный пример, но теперь с атрибутом href.
Пример с атрибутом href
Пример, в котором нам нужно получить значение ссылки так, как оно было установлено в HTML.
В этом примере атрибут href и DOM-свойство href содержат разные значения. В атрибуте href — то, что мы установили в коде, а в DOM-свойстве — полный URL. Это различие продиктовано стандартом, в соответствии с которым браузер должен привести значение href к полному URL.
Поэтому если нам нужно получить то, что находится в атрибуте, то без метода getAttribute в этом случае не обойтись.
В завершении разберём ещё атрибут selected .
Пример с атрибутом selected
Пример, в котором показано как можно получить значение выбранной опции select :
Пример, в котором показано как можно получить выбранные значения опций в элементе select :
Ещё один способ работы с атрибутами (свойство attributes)
В JavaScript у каждого элемента имеется свойство attributes , с помощью которого можно получить все его атрибуты в виде объекта NamedNodeMap .
Данный способ может находить применение, когда вам нужно, например перебрать все атрибуты элемента.
Доступ к атрибуту в этой коллекции осуществляется по его индексу или с помощью метода item . Отсчёт атрибутов в этой коллекции ведётся с 0.
Например, выведем в консоль все атрибуты некоторого элемента:
Кроме этого, работать с этой коллекцией можно также посредством следующих методов :
- .getNamedItem(‘имя_aтpибyтa’) – получает значение указанного атрибута (если указанный атрибут отсутствует у элемента, то в качестве результата получим null ).
- .setNamedItem(‘aтpибyт_yзeл’) – добавляет новый атрибут к элементу или обновляет значение у существующего. Для создания атрибута необходимо использовать метод document.createAttribute() , которому в качестве параметра необходимо передать имя атрибута. После этого созданному атрибуту необходимо присвоить значение с помощью свойства value .
- .removeNamedItem(‘имя_атрибута’) – удаляет указанный атрибут у элемента (в качестве результата возвращает удалённый атрибут).
Пример, работы с атрибутами через методы getNamedItem, setNamedItem и removeNamedItem: