Делегирование событий
Всплытие и перехват событий позволяет реализовать один из самых важных приёмов разработки – делегирование.
Идея в том, что если у нас есть много элементов, события на которых нужно обрабатывать похожим образом, то вместо того, чтобы назначать обработчик каждому, мы ставим один обработчик на их общего предка.
Из него можно получить целевой элемент event.target , понять на каком именно потомке произошло событие и обработать его.
Рассмотрим пример – диаграмму Ба-Гуа. Это таблица, отражающая древнюю китайскую философию.
Её HTML (схематично):
Квадрат Bagua: Направление, Элемент, Цвет, Значение . Северо-Запад. . . . ещё 2 строки такого же вида. . ещё 2 строки такого же вида.
В этой таблице всего 9 ячеек, но могло бы быть и 99, и даже 9999, не важно.
Он будет использовать event.target , чтобы получить элемент, на котором произошло событие, и подсветить его.
Код будет таким:
let selectedTd; table.onclick = function(event) < let target = event.target; // где был клик? if (target.tagName != 'TD') return; // не на TD? тогда не интересует highlight(target); // подсветить TD >; function highlight(td) < if (selectedTd) < // убрать существующую подсветку, если есть selectedTd.classList.remove('highlight'); >selectedTd = td; selectedTd.classList.add('highlight'); // подсветить новый td >
Однако, у текущей версии кода есть недостаток.
Северо-Запад .
Естественно, если клик произойдёт на элементе , то он станет значением event.target .
Вот улучшенный код:
table.onclick = function(event) < let td = event.target.closest('td'); // (1) if (!td) return; // (2) if (!table.contains(td)) return; // (3) highlight(td); // (4) >;
Применение делегирования: действия в разметке
Есть и другие применения делегирования.
Например, нам нужно сделать меню с разными кнопками: «Сохранить (save)», «Загрузить (load)», «Поиск (search)» и т.д. И есть объект с соответствующими методами save , load , search … Как их состыковать?
Первое, что может прийти в голову – это найти каждую кнопку и назначить ей свой обработчик среди методов объекта. Но существует более элегантное решение. Мы можем добавить один обработчик для всего меню и атрибуты data-action для каждой кнопки в соответствии с методами, которые они вызывают:
Обработчик считывает содержимое атрибута и выполняет метод. Взгляните на рабочий пример:
Сохранить
Обратите внимание, что метод this.onClick в строке, отмеченной звёздочкой (*) , привязывается к контексту текущего объекта this . Это важно, т.к. иначе this внутри него будет ссылаться на DOM-элемент ( elem ), а не на объект Menu , и this[action] будет не тем, что нам нужно.
Так что же даёт нам здесь делегирование?
- Не нужно писать код, чтобы присвоить обработчик каждой кнопке. Достаточно просто создать один метод и поместить его в разметку.
- Структура HTML становится по-настоящему гибкой. Мы можем добавлять/удалять кнопки в любое время.
Мы также можем использовать классы .action-save , .action-load , но подход с использованием атрибутов data-action является более семантичным. Их можно использовать и для стилизации в правилах CSS.
Приём проектирования «поведение»
Делегирование событий можно использовать для добавления элементам «поведения» (behavior), декларативно задавая хитрые обработчики установкой специальных HTML-атрибутов и классов.
Приём проектирования «поведение» состоит из двух частей:
- Элементу ставится пользовательский атрибут, описывающий его поведение.
- При помощи делегирования ставится обработчик на документ, который ловит все клики (или другие события) и, если элемент имеет нужный атрибут, производит соответствующее действие.
Поведение: «Счётчик»
Например, здесь HTML-атрибут data-counter добавляет кнопкам поведение: «увеличить значение при клике»:
Счётчик: Ещё счётчик:
Если нажать на кнопку – значение увеличится. Конечно, нам важны не счётчики, а общий подход, который здесь продемонстрирован.
Элементов с атрибутом data-counter может быть сколько угодно. Новые могут добавляться в HTML-код в любой момент. При помощи делегирования мы фактически добавили новый «псевдостандартный» атрибут в HTML, который добавляет элементу новую возможность («поведение»).
Всегда используйте метод addEventListener для обработчиков на уровне документа
Когда мы устанавливаем обработчик событий на объект document , мы всегда должны использовать метод addEventListener , а не document.on , т.к. в случае последнего могут возникать конфликты: новые обработчики будут перезаписывать уже существующие.
Для реального проекта совершенно нормально иметь много обработчиков на элементе document , установленных из разных частей кода.
Поведение: «Переключатель» (Toggler)
Ещё один пример поведения. Сделаем так, что при клике на элемент с атрибутом data-toggle-id будет скрываться/показываться элемент с заданным id :
Ваша почта:
Ещё раз подчеркнём, что мы сделали. Теперь для того, чтобы добавить скрытие-раскрытие любому элементу, даже не надо знать JavaScript, можно просто написать атрибут data-toggle-id .
Это бывает очень удобно – не нужно писать JavaScript-код для каждого элемента, который должен так себя вести. Просто используем поведение. Обработчики на уровне документа сделают это возможным для элемента в любом месте страницы.
Мы можем комбинировать несколько вариантов поведения на одном элементе.
Шаблон «поведение» может служить альтернативой для фрагментов JS-кода в вёрстке.
Итого
Делегирование событий – это здорово! Пожалуй, это один из самых полезных приёмов для работы с DOM.
Он часто используется, если есть много элементов, обработка которых очень схожа, но не только для этого.
- Вешаем обработчик на контейнер.
- В обработчике проверяем исходный элемент event.target .
- Если событие произошло внутри нужного нам элемента, то обрабатываем его.
- Упрощает процесс инициализации и экономит память: не нужно вешать много обработчиков.
- Меньше кода: при добавлении и удалении элементов не нужно ставить или снимать обработчики.
- Удобство изменений DOM: можно массово добавлять или удалять элементы путём изменения innerHTML и ему подобных.
Конечно, у делегирования событий есть свои ограничения:
- Во-первых, событие должно всплывать. Некоторые события этого не делают. Также, низкоуровневые обработчики не должны вызывать event.stopPropagation() .
- Во-вторых, делегирование создаёт дополнительную нагрузку на браузер, ведь обработчик запускается, когда событие происходит в любом месте контейнера, не обязательно на элементах, которые нам интересны. Но обычно эта нагрузка настолько пустяковая, что её даже не стоит принимать во внимание.
Задачи
Спрячьте сообщения с помощью делегирования
важность: 5
Дан список сообщений с кнопками для удаления [x] . Заставьте кнопки работать.
В результате должно работать вот так:
P.S. Используйте делегирование событий. Должен быть лишь один обработчик на элементе-контейнере для всего.
Как определить элемент, по которому был совершен клик?
Как с помощью jquery узнать, по какому элементу произошел клик?
Ну типа щелкнули по id_6, в консоль вывелось, что типа щелчок по элементу:id_6.
причем это не должно трогать остальные а на сстранице только те у которых класс с ID начинается
- Вопрос задан более трёх лет назад
- 13259 просмотров
Комментировать
Решения вопроса 1

$(document).on('click', 'a[class^="id"]', function(e) < e.preventDefault(); console.log(e.target); console.log(this); alert('Вы кликнули по ссылке с классом ' + this.className); >);
В e.target и this — элемент, по которому кликнули.
Делегирование поможет поймать клик на динамически создаваемых элементах
Ответ написан более трёх лет назад
Нравится 4 4 комментария
Михаил @nextel Автор вопроса
Спасибо)))) а я мучился, перменную то в колбек кто будет передавать?))) можете русским языком обьяснить что делает e.preventDefault(); отменяет поведение по умолчанию?
Как получить класс элемента по которому кликнули в jQuery
Вы можете просто использовать свойство event.target , чтобы получить класс из любого элемента, который был нажат в документе в jQuery. В этом решении нет необходимости заранее знать элемент.
Давайте попробуем следующий пример, чтобы понять, как это работает:
jQuery Get Class of Clicked Element

Читайте также
Похожие примеры:
- Как получить список классов элемента с помощью jQuery
- Как перебирать элементы с одним и тем же классом в jQuery
- Как выбрать элемент с несколькими классами с помощью jQuery
Как узнать позицию элемента, на который нажали?
Допустим есть родительский блок. В нем еще 5 блоков. Вот нужно получить номер позиции дочернего элемента, на который нажали мышкой. Чистый javascript.
- Вопрос задан более трёх лет назад
- 1277 просмотров
Комментировать
Решения вопроса 2
1. получить родителя
2. получить всех его чилдов
3. пробежать циклом по ним, сравнивая не похож ли чилд на event.target
4. если похож, то вернуть итератор
Ответ написан более трёх лет назад
Нравится 1 1 комментарий
fajjet @fajjet Автор вопроса
Самое простое и самое действенное))

°•× JavaScript Developer ^_^ ו°
Решением был отмечен велосипед, прискорбно.
roditel.addEventListener("click", function(event)< [].indexOf.call(this.children, event.target); >);
Ответ написан более трёх лет назад
Нравится 1 3 комментария
fajjet @fajjet Автор вопроса
можно немного подробнее. Я не совсем понимаю данный синтаксис. Особенно пустой айдишник в начале

fajjet: если уклоняться от разжеванного ответа, то мы добавляем псевдомассиву this.children метод массивов indexOf. Потом вызываем его, просим найти индекс кликнутого элемента в списке детей родительского блока.
Stalker_RED @Stalker_RED
Виталий Инчин: [] вместо Array.prototype? круто.
собрал демку https://jsfiddle.net/Stalk/jvo7mzwr/
Ответы на вопрос 2

Во время генерации блоков назначайте им какой-нибудь придуманный аттрибут, например myattr=»номер позиции» и просто возьмите его значение при обработке.
Ответ написан более трёх лет назад

А давно так можно?
Stalker_RED @Stalker_RED
Виталий Инчин: Всегда. DOM-элементам можно добавлять свои свойства, методы и т.п. Как и к почти всем другим объектам в javascript. Разве что в XHTML были какие-то ограничения. Но ваш вариант лучше, имхо.

Stalker_RED: я не про свойства, а про как попало названные атрибуты.
Stalker_RED @Stalker_RED
Виталий Инчин: в большинстве браузеров можно. https://jsfiddle.net/Stalk/6x098ro6/
Возможно с «всегда» я загнул, но примерно со времен ие6, а то и раньше.
Т.е. как можно — валидатор вероятно будет ругаться, но браузер от этих левых свойств не упадет, и к ним можно достучаться через js.

Stalker_RED: в том то и дело, что валидатор ругается на них и «можно» то оно можно, как и пользовательские теги, но для кого тогда стандарты? Для кого data-атрибуты?
Stalker_RED @Stalker_RED
Виталий Инчин: Можно так, чтобы не ругался stackoverflow.com/questions/992115/custom-attribut.
Но я согласен, в большинстве случаев хватает возможности data-*
А в данном примере они вообще не нужны.