Введение в DOM.
DOM (Document Object Model) - внутренние объекты и функции браузера, хранящие состояние страницы.JS через это API имеет полный доступ ко всему тому, что есть в HTML и CSS.
Корень
Корнем дерева элементов DOM является объект document
Поиск элементов
Что бы найти элемент, нужно обратится к методу document
, или любого другого элемента, в котором нужно что-то найти:
1var el = document.getElementById("someId"); //обратите внимания, без #2//в последних браузерах все `id` автоматически попадают в глобальную область видимости (в объект window)3var el2 = document.querySelector("#someId"); //поиск по любому селектору, аналог jQuery4var el3 = document.querySelectorAll("a"); //поиск всех тегов a
Создание элементов DOM
1document.createElement("a"); //обратите внимание, без <>
Добавление элементов:
1var tr = document.createElement("tr");2var td = document.createElement("td");3var td2 = document.createElement("td");45tr.appendChild(td); //добавление ячейки в конец строки таблицы.6tr.insertBefore(td2, tr.childNodes[0]); //добавление ячейки перед первой ячейкой (в самое начало строки таблицы)
Ссылка на родительский элемент находится в свойстве parentElement
:
1tr.childNodes[0].parentElement === tr
Свойства объектов или наборов объектов элементов в 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, в том числе обычный текст и тот или иной тэг. Дочерние элементы каждого элемента находятся в псевдомассивеchildNodes
- В псевдомассиве
children
находятся только дочерние узлы-теги, но без обычного текста.
HTMLCollection это список узлов. Отдельный узел может быть доступен по порядковому номеру или имени узла и атрибута. Коллекции в HTML DOM являются живыми. Коллекции обновляются при изменении документа. Коллекция HTML всегда находится в DOM, в то время как NodeList является более универсальной конструкцией, которая может или не может быть в DOM.
NodeList является коллекцией узлов. NodeList обеспечивает уровень абстракции над коллекцией узлов, не определяя и не ограничивая как это коллекция используется. Объекты NodeList являются “живыми” или статическим, в зависимости от способа используемого для их получения.
События
Каждый элемент DOM содержит множество свойств on...
, в которые вы можете занести тот или иной обработчик события:
1document.onmousemove = function(){2 document.write("mouse move <br/>");3}
Так же можно добавлять обработчики используя метод элемента addEventListener
:
1document.addEventListener("mousemove",function(){2 document.write("mouse move <br/>");3});
Как было уже отмечено, DOM - это дерево из элементов верстки страницы, каждый из которых представлен объектом Javascript. Кроме данных, в объектах бывают те или иные действия, которые объект может осуществлять - методы.
Установка обработчиков
Для обработки событий вы можете назначить функцию-обработчик события путем присвоения ключа объекта:
1var element = document.getElementById('root')2element.onmousemove = function(event){3 console.log(this, event);4}
Так же вы можете добавить несколько обработчиков на одно и тоже событие в одном и том же элементе, используя метод addEventListener
:
1var element = document.getElementById('root')23function eventHandler(event){4 console.log(this, event);5}678element.addEventListener("mousemove",eventHandler);9element.addEventListener("click",eventHandler);
Данные, передаваемые в обработчик
В обработчик события передается два аргумента:
this
, который ссылается на элемент, на который "навешен" обработчик. Используяthis
вы можете узнать любую информацию о элементе и навешивать один и тот же обработчик на несколько элементов, если вам нужно схожее поведение на многих элементах.event
, объект-событие, переданный первым аргументом в функцию-обработчик (вы можете назвать его как угодно). С помощью этого объекта можно узнать информацию о событии (нажатые кнопки мыши и клавиатуры, положение курсора мыши, и т. п.), а так же управлять обработкой потока событий:event.stopPropagation()
останавливает всплытие событий (например,click
всплывает от вложенных элементов к обрамляющим). Обрамляющие элементы не узнают о событии.event.stopImmediatePropagation()
аналогичен предыдущему, однако не будут запущены даже следующие обработчики на этом элементеevent.preventDefault()
запрещает браузеру запускать обработчики по умолчанию (отправку данных дляsubmit
формы, переход по клику на ссылку<a>
и т. п.)
addEventListener
Первый аргумент метода addEventListener - это тип события ( строка ), например: "mouseover" "mouseout" "input" "change" ... Второй аргумент - ссылка на функцию ( обработчика события ) Третий аргумент - логическое значение - будучи установленным в true, позволяет перехватить событие на фазу погружения ( capturing )
1var element = document.getElementById('root')23function eventHandler(event){4 console.log(this, event);5}
removeEventListener
Прослушивателей событий нужно удалять, поскольку они не убираются автоматически при удалении элемента
При удалении нужно передавать точно такие же аргументы, какие были переданы методу addEventListener при создании прослушивателя
🚫 Не правильно
1document.getElementById ( 'sample' )2 .addEventListener ( 'click', function ( event ) {3 console.log ( 'sample click event: ', event )4 })5document.getElementById ( 'sample' )6 .removeEventListener ( 'click', function ( event ) {7 console.log ( 'sample click event: ', event )8 })
✅ Правильно
1var elem = document.getElementById ( 'element' )2function clickHandler ( event ) {3 this.innerHTML = '<small>My content was changed!</small>'4}5elem.addEventListener ( 'click', clickHandler )6elem.removeEventListener ( 'click', clickHandler )
Всплытие событий
1<div onclick="alert('Hello!')">2 <em>Если вы кликните на <code>EM</code>, сработает обработчик на <code>DIV</code></em>3</div>
Всплытие
Принцип всплытия очень простой. Когда на элементе происходит событие, обработчики сначала срабатывают на нём, потом на его родителе, затем выше и так далее, вверх по цепочке предков.
Например, есть 3 вложенных элемента FORM
> DIV
> P
с обработчиком на каждом
1<form onclick="alert('form')">FORM2 <div onclick="alert('div')">DIV3 <p onclick="alert('p')">P</p>4 </div>5</form>
Клик по внутреннему <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 вот так:
1var elem = document.getElementById('root');2function handler(){}3elem.addEventListener(handler, {capture: true})4// или просто "true", как сокращение для {capture: true}5elem.addEventListener(handler, true)
Существуют два варианта значений опции capture:
- Если аргумент
false
(по умолчанию), то событие будет поймано при всплытии. - Если аргумент
true
, то событие будет перехвачено при погружении.
Обратите внимание, что хоть и формально существует 3 фазы, 2-ую фазу («фазу цели»: событие достигло элемента) нельзя обработать отдельно, при её достижении вызываются все обработчики: и на всплытие, и на погружение.
Давайте посмотрим и всплытие и погружение в действии:
Пример 2
Здесь обработчики навешиваются на каждый элемент внутри элемента с id ex2
, чтобы увидеть в каком порядке они вызываются по мере прохода события.
Если вы кликните по <p>
, то последовательность следующая:
FORM → DIV → p (фаза погружения, первый обработчик)
P (фаза цели, срабатывают обработчики, установленные и на погружение и на всплытие, так что выведется два раза)
P→ DIV → FORM (фаза всплытия, второй обработчик)
Существует свойство event.eventPhase
, содержащее номер фазы, на которой событие было поймано. Но оно используется редко, мы обычно и так знаем об этом в обработчике.
Важно
Чтобы убрать обработчик removeEventListener
, нужна та же фаза
Если мы добавили обработчик вот так addEventListener(..., true)
, то мы должны передать то же значение аргумента capture в removeEventListener(..., true)
, когда снимаем обработчик.