Браузерные события, часть 3 из 5. Делегирование
25.04.2022
Теги: DOM • Frontend • HTML • JavaScript • Web-разработка • Событие • Теория
Делегирование возможно благодаря тому, что события всплывают. А это означает, что оно возникает не только на самом элементе, но затем на его родителе, потом на родителе его родителя и так далее. Таким образом, при добавлении обработчика одному из предков, он будет выполняться всякий раз, когда это событие будет происходит на любом из его потомков. В обработчике получить элемент, который инициировал это событие, можно посредством свойства target
объекта event
.
<ul id="list"> <li>Раз</li> <li>Два</li> <li>Три</li> </ul>
Предположим, что при нажатии на каждый элемент списка должно что-то произойти. Мы можем добавить отдельный обработчик для каждого <li>
. Но что, если у нас элементы <li>
динамически добавляются и удаляются из этого списка? В этом случае лучшим решением будет добавить обработчик к родительскому элементу <ul>
.
document.querySelector('#list').addEventListener('click', function (event) { if (event.target && event.target.nodeName === 'LI') { alert(event.target.textContent); } });
Пример делегирования
Есть и другие применения делегирования. Например, нам нужно сделать меню с разными кнопками: «Сохранить», «Загрузить», «Поиск» и т.д. И есть объект с соответствующими методами save
, load
, search
. Как их состыковать?
Первое, что может прийти в голову — это найти каждую кнопку и назначить ей свой обработчик среди методов объекта. Но существует более элегантное решение. Мы можем добавить один обработчик для всего меню и атрибуты data-action
для каждой кнопки в соответствии с методами, которые они вызывают.
<div id="menu"> <button data-action="save">Сохранить</button> <button data-action="load">Загрузить</button> <button data-action="search">Поиск</button> </div> <script> class Menu { constructor(elem) { this._elem = elem; elem.onclick = this.onClick.bind(this); } save() { alert('Сохранить'); } load() { alert('Загрузить'); } search() { alert('Поиск'); } onClick(event) { let action = event.target.dataset.action; if (action) { this[action](); } } } new Menu(document.querySelector('#menu')); </script>
Обратите внимание, что метод this.onClick
в конструктору привязывается к контексту текущего объекта this
. Это важно, т.к. иначе this
внутри него будет ссылаться на DOM-элемент elem
, а не на объект Menu
, и this[action]
будет не тем, что нам нужно.
Поведение элементов
Делегирование событий можно использовать для добавления элементам «поведения» (behavior), декларативно задавая хитрые обработчики установкой специальных html-атрибутов и классов. Для этого на документ устанавливается обработчик, который ловит все клики (или другие события) и, если элемент имеет нужный атрибут, производит соответствующее действие.
1. Поведение «Счётчик»
Например, здесь html-атрибут data-counter
добавляет кнопкам поведение — увеличить значение при клике:
Счетчик раз <input type="button" value="1" data-counter> Счетчик два <input type="button" value="2" data-counter> Счетчик три <input type="button" value="3" data-counter> <script> document.addEventListener('click', function(event) { if (event.target.dataset.counter !== undefined) { event.target.value++; } }); </script>
Элементов с атрибутом data-counter
может быть сколько угодно, новые могут добавляться в html-код в любой момент. При помощи делегирования мы фактически добавили новый «псевдостандартный» атрибут в HTML, который добавляет элементу новую возможность («поведение»).
document
, мы всегда должны использовать метод addEventListener
, а не document.onclick
, т.к. в случае последнего могут возникать конфликты — новые обработчики будут перезаписывать уже существующие.
2. Поведение «Переключатель»
Сделаем так, что при клике на элемент с атрибутом data-toggle-id
будет скрываться/показываться элемент с заданным id
:
<button data-toggle-id="subscribe-mail"> Показать форму подписки </button> <form id="subscribe-mail" hidden> Ваша почта: <input type="email" name="email" value=""> </form> <script> document.addEventListener('click', function(event) { let id = event.target.dataset.toggleId; if (id) { let elem = document.getElementById(id); elem.hidden = !elem.hidden; } }); </script>
Теперь для того, чтобы добавить скрытие-раскрытие любому элементу, даже не надо знать JavaScript, можно просто написать атрибут data-toggle-id
. Это очень удобно — не нужно писать js-код для каждого элемента, который должен так себя вести, просто используем поведение. Обработчики на уровне документа сделают это возможным для элемента в любом месте страницы.
- Браузерные события, часть 5 из 5. Генерация пользовательских событий
- Браузерные события, часть 4 из 5. Действие браузера по умолчанию
- Браузерные события, часть 2 из 5. Погружение и всплытие
- Браузерные события, часть 1 из 5. Основные сведения
- DOM, часть 4 из 4. Атрибуты и свойства
- DOM, часть 3 из 3. Поиск элементов
- DOM, часть 2 из 3. Изменение документа
Поиск: DOM • Frontend • HTML • JavaScript • Web-разработка • Событие • Теория • Event