Что такое делегирование событий js
Делегирование событий
Всплытие и перехват событий позволяет реализовать один из самых важных приёмов разработки – делегирование.
Идея в том, что если у нас есть много элементов, события на которых нужно обрабатывать похожим образом, то вместо того, чтобы назначать обработчик каждому, мы ставим один обработчик на их общего предка.
Рассмотрим пример – диаграмму Ба-Гуа. Это таблица, отражающая древнюю китайскую философию.
Её HTML (схематично):
В этой таблице всего 9 ячеек, но могло бы быть и 99, и даже 9999, не важно.
Наша задача – реализовать подсветку ячейки
Вместо того, чтобы назначать обработчик onclick для каждой ячейки
из таблицы динамически в любое время, и подсветка будет стабильно работать. Однако, у текущей версии кода есть недостаток. Клик может быть не на теге | , а внутри него. В нашем случае, если взглянуть на HTML-код таблицы внимательно, видно, что ячейка | содержит вложенные теги, например : Внутри обработчика table.onclick мы должны по event.target разобраться, был клик внутри или нет. | Вот улучшенный код: В итоге мы получили короткий код подсветки, быстрый и эффективный, которому совершенно не важно, сколько всего в таблице . | Применение делегирования: действия в разметкеЕсть и другие применения делегирования. Первое, что может прийти в голову – это найти каждую кнопку и назначить ей свой обработчик среди методов объекта. Но существует более элегантное решение. Мы можем добавить один обработчик для всего меню и атрибуты data-action для каждой кнопки в соответствии с методами, которые они вызывают: Обработчик считывает содержимое атрибута и выполняет метод. Взгляните на рабочий пример: Так что же даёт нам здесь делегирование? Приём проектирования «поведение»Делегирование событий можно использовать для добавления элементам «поведения» (behavior), декларативно задавая хитрые обработчики установкой специальных HTML-атрибутов и классов. Приём проектирования «поведение» состоит из двух частей: Поведение: «Счётчик»Например, здесь HTML-атрибут data-counter добавляет кнопкам поведение: «увеличить значение при клике»: Если нажать на кнопку – значение увеличится. Конечно, нам важны не счётчики, а общий подход, который здесь продемонстрирован. Элементов с атрибутом data-counter может быть сколько угодно. Новые могут добавляться в HTML-код в любой момент. При помощи делегирования мы фактически добавили новый «псевдостандартный» атрибут в HTML, который добавляет элементу новую возможность («поведение»). Поведение: «Переключатель» (Toggler)Ещё один пример поведения. Сделаем так, что при клике на элемент с атрибутом data-toggle-id будет скрываться/показываться элемент с заданным id : Это бывает очень удобно – не нужно писать JavaScript-код для каждого элемента, который должен так себя вести. Просто используем поведение. Обработчики на уровне документа сделают это возможным для элемента в любом месте страницы. Мы можем комбинировать несколько вариантов поведения на одном элементе. Шаблон «поведение» может служить альтернативой для фрагментов JS-кода в вёрстке. ИтогоДелегирование событий – это здорово! Пожалуй, это один из самых полезных приёмов для работы с DOM. Он часто используется, если есть много элементов, обработка которых очень схожа, но не только для этого. Конечно, у делегирования событий есть свои ограничения: ЗадачиСпрячьте сообщения с помощью делегированияВ результате должно работать вот так: P.S. Используйте делегирование событий. Должен быть лишь один обработчик на элементе-контейнере для всего. Раскрывающееся деревоСоздайте дерево, которое по клику на заголовок скрывает-показывает потомков: Решение состоит из двух шагов: Сортируемая таблицаСделать таблицу сортируемой: при клике на элемент строки таблицы должны сортироваться по соответствующему столбцу. | Каждый элемент имеет атрибут data-type: | В примере выше первый столбец содержит числа, а второй – строки. Функция сортировки должна это учитывать, ведь числа сортируются иначе, чем строки. P.S. Таблица может быть большой, с любым числом строк и столбцов. Поведение «подсказка»Напишите JS-код, реализующий поведение «подсказка». Пример HTML с подсказками: Результат в ифрейме с документом: В этой задаче мы полагаем, что во всех элементах с атрибутом data-tooltip – только текст. То есть, в них нет вложенных тегов (пока). Для решения вам понадобятся два события: После реализации поведения – люди, даже не знакомые с JavaScript смогут добавлять подсказки к элементам. Делегирование событий в JSНа прошлом уроке мы научились вешать обработчик события на одну кнопку в JS. Однако методы, хорошо работающие с единичными объектами, совершенно не эффективны для массовых манипуляций. Пример из реальной жизни: Директор школы не подходит к каждому новому ученику, чтобы сообщить ему о школьных правилах. Эту почетную миссию он делегирует классному руководителю, чтобы каждый новый ученик знал общие правила. На странице имеется какое-то количество навигационных ссылок, на которых при клике будут срабатывать одинаковые события. Навесить на каждую кнопку по событию не так и сложно. Но есть одна проблема. А что если пользователь сайта (заказчик) добавит через админку новую ссылку. У добавленной ссылки уже не будет события. Делегирование событий обезопасит вас от подобных косяков. Правильно будет работать сразу с родительским элементом, а не с каждым дочерним по отдельности. Тогда каждый новый добавленный элемент в родительский блок, автоматически унаследует и его событие. Пример с делегированием в JSПеред нами обычный список, где теги li являются дочерними для тега ul. Мы будем работать с родителем всех li. let ul = document.querySelector(‘ul’); Навешиваем обработчик события addEventListener на родителя и первым параметром передаем событие клик (можно любое событие). Вторым параметром передаем callback функцию с передаваемым объектом события (event), чтобы свойства объекта могли вызываться внутри, функции и выведем их в консоль. Нас интересует, как в DOM прописано значение у свойства tagName. ul.addEventListener(‘click’, function(event) < Свойства выведутся в консоль при клике по списку. Раскрываем всю эту цепочку MouseEvent → target → tagName. Дословный перевод: событие мыши → цель → имя тега. Начинающие разработчики часто заглядывают в консоль, поскольку визуализируя процесс, проще понять смысл происходящего и увидеть ошибки. Суть делегирования событийНам нужно проверить, куда кликнул пользователь, если он кликнул по списку, то выполнится какое-то действие. if(event.target && event.target.tagName == ‘LI’) < Нас не интересуют только клики по пунктам списка. Если целью события клика event, оказался тег LI, то в консоль выведется слово Привет! В первой части условия event.target мы проверяем, есть ли у объекта события свойство target. Во второй части условия, после оператора AND, мы проверяем, а действительно ли кликнули по тегу li. Если элемент подходит под условие, то на нем сработает та функция (набор действий), которую мы укажем в теле. Теперь при добавлении новых пунктов меню, можно не беспокоиться, что они не будут реагировать на события. Пример кода целиком. let ul = document.querySelector(‘ul’); Делегирование событий в JS, нужно обязательно применять, если на странице есть много элементов с одним и тем же обработчиком события. Всплытие событий в JSКопирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)! Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov. Если Вы не хотите пропустить новые материалы на сайте, Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы. Порекомендуйте эту статью друзьям: Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте): Комментарии ( 0 ):Для добавления комментариев надо войти в систему. Copyright © 2010-2021 Русаков Михаил Юрьевич. Все права защищены. Делегирование событий в JavaScriptОчень доходчивая статья о том, как работает делегирование событий в JavaScript и как не запутаться в слушателях событий. Чтобы понять делегирование событий в JavaScript, вам сначала надо понять работу слушателей самих событий (ну или event listeners). Я не очень люблю описывать вещи словами из их названия, но тут уж некуда деваться, слушатели событий следят за срабатыванием самих событий на странице. Вот, собственно и всё. Событием в JavaScript можно назвать то, что “происходит с HTML элементом”, а происходить с ним может много чего. Вот некоторые популярные JavaScript события:change — срабатывает тогда, когда что-то поменялось в HTML элементе. click — когда пользователь кликает на элемент. mouseover — когда пользователь наводит мышь на HTML элемент. mouseout — когда пользователь отводит мышку от элемента. keydown — когда пользователь кликает на клавиатуру. load — когда браузер заканчивает загрузку страницы. Чтобы добавить слушатель событий к HTML элементу, вы можете использовать addEventListner() метод. Первая часть кода это то, что мы будем слушать, в нашем случае это просто HTML элемент. Но в JavaScript можно слушать просто неописуемое изобилие вещей на странице и не только. Это может быть HTML элемент на странице, это может быть сам документ или это может быть окно браузера. В общем, это объект высшего порядка в client-side JavaScript который охватывает почти всё. Но всё же, в большинстве случаев это HTML элементы. Вторая часть кода это уже сам слушатель. Вот как работает eventListener :Слушатели событий выставляются после загрузки страницы. Так что, когда вы впервые открываете сайт, браузер скачивает, считывает и выполняет JavaScript. В коде выше, при загрузке страницы, слушатель событий находит HTML элемент с id disnay-character и выставляет на него слушатель события по клику. Вся эта конструкция будет успешно работать, если элемент уже существует на странице. Но что происходит со слушателем событий, когда элемент добавляется в DOM уже после загрузки страницы? Делегирование событийИменно оно решает эту проблему. Чтобы понять принцип его работы, нам надо посмотреть ниже на список персонажей Disney.
У этого списка есть довольно простой функционал. Именно для наших нужд мы можем добавить несколько персонажей в этот список и проверить боксы рядом с именем персонажа. Этот список также является динамическим. Инпуты (Mickey, Minnie, Goofy) были добавлены уже ПОСЛЕ загрузки страницы и следовательно, на них не были прикреплены слушатели событий. Давайте посмотрим на этот код: Но давайте посмотрим на HTML при загрузке страницы: А теперь давайте взглянем на HTML после загрузки страницы (из локального веб-хранилища, API запроса и т.п.): Если вы захотите кликнуть на инпуты персонажей — Mickey, Minnie, or Goody), то вы наверное ожидали бы увидеть всплывающее окно с надписью “hi!”, но так как они не были загружены на страницу при её инициализации, то и прослушиватели событий НЕ БЫЛИ добавлены на эти элементы и само собой ничего не произойдёт.
Как же пофиксить эту проблему?Суть делегирования событий в том, что вместо прослушивания изменений на инпутах напрямую, нам нужно прослушивать HTML элемент, который был бы на странице при изначальной загрузке. Лучше рассматривать делегирование событий, как ответственных родителей и нерадивых детишек. В этом случае родители это просто боги, а детям приходиться слушать всё, что говорят их родители. Прекрасный аспект тут в том, что если бы мы добавили больше детей (или инпутов), то родители бы остались такими же в правах — они были с самого начала или другими словами, прямо с загрузки страницы. Давайте прикрепим слушатель событий:
event.target это отсылка к объекту, на котом сработало событие. Или другими словами, он указывает на HTML элемент на котором сработало событие. ** label рассматривается, как часть объекта input — поэтому мы видимо их обоих** Console.log(event.currentTarget)Если мы выведем в консоль event.currentTarget — то увидим мы кое что другое.
Пишем делегирование событий в JavaScriptТак как теперь мы знаем, что event.target указывает на элементы на которых отработало событие и также мы знаем, какой элемент нам нужно слушать, решение проблемы с делегированием для нас не является проблемой. В общем, код выше говорит нам то, что если элемент на котором отработал клик не является инпутом, то мы выходим из функции. А если этот элемент является инпутом, то тогда мы выводим в консоль event.target и выполняем последующий JavaScript код на этом узле потомке. Очень важно, что теперь мы можем быть уверены в том, что пользователь кликнул нужный узел потомок, пусть даже инпуты не были добавлены в DOM после начальной загрузки страницы. Event Bubbling (Всплытие событий)Если вы хотите завершить чтение на этом моменте — то смело это делайте. Мы уже узнали основы делегирования событий. Но для глубокого понимания того, почему оно работает, нам нужно понять Event Bubbling. Что происходит на самом деле при клике?Всякий раз, когда пользователь кликает, этот самый клик отдаётся вверх на самую высь DOM и отрабатывает событие клика на всех элементах родителя по которому был сделан клик. Вы не всегда видите эти клики, так как вы не всегда слушаете (с eventListener ) клики на этих элементах, но как бы то ни было, но такое “всплытие” действий имеет место быть. Это называется Event Bubbling (Всплытие событий) или распространением события. Это означает то, что всякий раз, когда вы кликаете на один из наших инпутов в DOM, вы по сути прокликиваете весь документ.
Выше мы видим то, что мы бы увидели в браузере. Обратите внимание на то, как мышка кликает по третьему div ’у. Как и ожидалось, когда я кликаю по нему, я вижу его класс в консоле. Но кликая по div #3, я также кликаю и по div #2 и div#1, который вывелись в консоль. Это и называется всплытием событий. Мы видим каждый класс, потому что мы добавили прослушиватель событий каждому родительскому div ’у.
Но вернемся к нашему примеру с делегированием события: В завершение. А зачем использовать делегирование событий?Email подписка!Мы забыли про делегирование в JavaScript. Event delegation in ReactВсем привет. Статья о делегирование событий в JavaScript и реализация его в react.js.О чем собственно речь? Зачем и почему? Для начала давайте кратко обсудим:И в конце: почему не надо забывать об делегировании в React. СобытиеJavaScript с HTML взаимодействуют между собой за счёт событий (events). Каждое событие служит для того, чтобы сказать JavaScript’у о том, что в документе или окне браузера что-то произошло. Для того чтобы отловить эти события нам нужны слушатели (listeners), этакие обработчики, которые запускаются в случае возникновения события. Распространение событийПорядок. Решая проблему: как понять, какой части страницы принадлежит событие? Было реализовано два способа: в Internet Explorer — “всплытие событий”, а в Netscape Communicator — “перехват событий”. Всплытие событийВ данном случае событие срабатывает у самого глубокого узла в дереве документа, после поднимается по иерархии до самого window. В этом случае будет такой порядок:
В случае с перехватом событий работает все наоборот:
В итоге мы имеем такую структуру распространения DOM-событий: Делится эта схема на три фазы: фаза перехвата — событие можно перехватить до попадания на элемент, фаза цели — обработка целевым элементом и фаза всплытия — что бы выполнить какие-либо заключительные действия в ответ на событие. Итак, переходим к обработке событийПосмотрим типичный пример обработки события в JavaScript. Все бы нечего, но тут мы вспоминаем про наш любимы IE который подписывается на события с помощью attachEvent, а для удаления detachEvent. А еще можно подписываться на событие несколько раз. И не забываем что подписавшись анонимной функцией мы не имеем возможность отписаться. Но мы же не г*внокодеры. Сделаем все по канону: И т.д. и т.п. и вот эти все танцы. Хорошо мы молодцы, все проблемы решили, все ок. Правда код вышел довольно громоздким. А теперь представим, что нам нужно много подписок на множество элементов. Ух это займет не мало строк кода. Пример: И так для каждого элемента, и надо удаление не забыть, работа с таргет и тому подобное И тут к нам на помощь приходит делегирование событий (event delegation).Все что нам надо это подключить один единственный обработчик к наивысшей точке в DOM-дереве: В итоге у нас только один обработчик в памяти, а для нужного действия можно использовать свойство id. Меньшее потребление памяти повышает общее быстродействие страницы в целом. Для регистрации обработчика событий требуется меньше времени и меньше обращений к DOM. Исключение разве что mouseover и mouseout, с ними все немного сложнее. А теперь что насчёт ReactВсе что касается кросcбраузерности за нас уже все сделали ребята из facebook. Все наши обработчики событий получают экземпляр SyntheticEvent. Который заботится о нас повторно используя события из пула удаляя все свойства после вызова обработчика. Тем не менее лишний обработчик есть лишний обработчик. Несколько раз встречал, да и каюсь сам писал, такого рода код: В примере показан случай, когда есть какой-то лист с n-количеством элементов, а значит и с n-количеством регистраций обработчиков. Запустим зайдем на страницу и проверим сколько обработчиков сейчас в деле. Для этого я нашёл не плохой скрипт: Работает в dev-tool хрома. А теперь делегируем все это родительскому div элементу и ура, мы только что оптимизировали наше приложение в n=array.length раз. Пример код ниже: Делегирование хороший инструмент для обработки большого количества подписок, а в случае с динамичным рендером и частых перерисовок просто незаменим. Пожалейте ресурсы пользователя, они не безграничны. Статья написана на основе книги JavaScript для профессиональных веб-разработчиков, автор: Николас Закас. Спасибо большое за внимание. Если есть чем поделится или нашли какой-то недочет, может ошибку или просто есть вопрос, то пишите в комментариях. Буду рад любой обратной связи! Простое объяснение делегирования событий в JavaScriptПриветствую. Представляю вашему вниманию перевод статьи «A Simple Explanation of Event Delegation in JavaScript», опубликованной 14 июля 2020 года автором Dmitri Pavlutin В данной статье Дмитрий Павлутин объясняет, на чём основан один из базовых паттернов работы с DOM-событиями. 1. Почему делегирование событий?Давайте напишем скрипт, который при нажатии на HTML-кнопку, будет отправлять сообщение в консоль. Чтобы срабатывало такое поведение, необходимо в JavaScript найти эту кнопку и с помощью метода addEventListener() прикрепить к ней обработчик события. Данный способ позволяет начать отслеживать события на одном элементе. Например, на кнопке. А что, если возникает необходимость отслеживать события на множестве кнопок? Вот пример одного из способов реализаций: Посмотреть, как работает данный способ, можно в демонстрации CodeSandbox Сначала делается выборка всех необходимых кнопок страницы, затем с помощью цикла for (const button of buttons) производится обход всех элементов этого списка, в котором к каждой кнопке прикрепляется обработчик события. Также, когда во время работы с документом на странице появляются новые кнопки, возникает необходимость вручную прикреплять обработчики событий для этих новых элементов. Существует ли лучший способ? К счастью, при использовании шаблона «делегирование событий», отслеживание событий на множестве элементов требует наличия только одного обработчика. Делегирование событий использует особенности работы «распространения событий». Чтобы понять, как работает делегирование, предлагаю сначала разобраться в принципе работы их распространения. 2. Распространение событийКогда вы нажимаете на кнопку в следующей HTML-разметке: Событие клика распространяется в 3 этапа: Третий аргумент captureOrOptions метода addEventListener : позволяет вам перехватывать события на разных этапах их распространения. В следующем примере обработчик перехватывает событие клика на элементе на «Фазе захвата»: В демонстрации CodeSandbox, при нажатии на кнопку, в консоли можно увидеть, как распространяется событие. Итак, как распространение события помогает перехватывать события из множества кнопок? Принцип прост: обработчик события прикрепляется к элементу, являющемуся для кнопок родительским, и при нажатии на кнопку отлавливает всплывающее событие. Именно так работает делегирование событий. 3. Делегирование событийДавайте воспользуемся делегированием, чтобы отловить клики на несколько кнопок: Откройте демонстрационный код и кликните на любую кнопку – вы увидите в консоли сообщение «Click!». Идея делегирования событий роста. Вместо прикрепления обработчиков событий прямо к кнопкам, мы делегируем отслеживание этого события родительскому элементу Использование делегирования событий требует 3 шагов: Шаг 1. Определить общего родителя элементов для отслеживания событий Шаг 2. Прикрепить к родительскому элементу обработчик событий Теперь вы можете увидеть преимущества шаблона делегирования событий: вместо прикрепления обработчиков к каждой кнопке, как это было сделано раньше, благодаря делегированию событий, остаётся потребность только в одном обработчике. 4. РезюмеКогда происходит событие нажатия на кнопку (или любое другое распространяющееся событие): Механизм называется распространением события. Делегирование событий является полезным шаблоном, так как позволяет отслеживать события на множестве элементов с помощью только одного обработчика. Для работы делегирования событий нужно 3 шага:
|