Статьи

Делегирование событий JavaScript проще, чем вы думаете

Если вы хотите добавить немного интерактивности JavaScript на свои веб-страницы, возможно, вы слышали о делегировании событий JavaScript и думали, что это один из тех замысловатых шаблонов проектирования, о которых беспокоятся только хардкорные программисты JavaScript. Правда в том, что если вы уже знаете, как добавить обработчики событий JavaScript, это несложно реализовать.

События JavaScript — это основа всей интерактивности на веб-страницах (я имею в виду серьезную интерактивность, а не те изящные выпадающие меню CSS). В традиционной обработке событий вы добавляете или удаляете обработчики событий из каждого элемента по мере необходимости. Однако обработчики событий могут потенциально привести к утечкам памяти и снижению производительности — чем больше у вас есть, тем больше риск. Делегирование событий JavaScript — это простая техника, с помощью которой вы добавляете один обработчик событий в родительский элемент, чтобы избежать необходимости добавлять обработчики событий для нескольких дочерних элементов.

Как это работает?

При делегировании событий используются две часто пропускаемые функции событий JavaScript: всплывающее окно события и целевой элемент . Когда событие инициируется на элементе, например, щелчком мыши по кнопке, то же событие также запускается для всех предков этого элемента. Этот процесс известен как всплытие событий; событие всплывает от исходного элемента к вершине дерева DOM. Целевым элементом любого события является исходный элемент, кнопка в нашем примере, и он хранится в свойстве объекта события. Используя делегирование события, можно добавить обработчик события в элемент, дождаться, когда событие всплывет из дочернего элемента, и легко определить, из какого элемента произошло событие.

Как это поможет мне?

Представьте себе HTML-таблицу с 10 столбцами и 100 строками, в которой вы хотите, чтобы что-то происходило, когда пользователь щелкает ячейку таблицы. Например, однажды мне нужно было сделать каждую ячейку таблицы такого размера редактируемой при нажатии. Добавление обработчиков событий в каждую из 1000 ячеек было бы серьезной проблемой производительности и, возможно, источником утечек памяти при сбое браузера. Вместо этого, используя делегирование событий, вы добавляете только один обработчик событий к элементу table , перехватываете событие click и определяете, какая ячейка была нажата.

Как это выглядит в коде?

Код прост; нам нужно только беспокоиться об обнаружении целевого элемента. Допустим, у нас есть элемент table с идентификатором « report », и мы добавили в таблицу обработчик событий для события click , которое editCell функцию editCell . Функция editCell должна будет определить целевой элемент для события, которое всплыло в table . Ожидая, что мы напишем несколько функций-обработчиков событий, которые будут нуждаться в этой функции, мы getEventTarget ее в отдельную функцию с именем getEventTarget :

 function getEventTarget(e) { e = e || window.event; return e.target || e.srcElement; } 

Переменная e представляет объект события, и нам нужно только разбросать кросс-браузерный код, чтобы получить доступ и вернуть целевой элемент, сохраненный в свойстве srcElement в Internet Explorer и свойстве target в других браузерах.

Далее editCell функция editCell которая вызывает функцию getEventTarget . Как только мы получим ссылку на целевой элемент, мы должны убедиться, что этот элемент именно тот, который мы ожидаем:

 function editCell(e) { var target = getEventTarget(e); if(target.tagName.toLowerCase() === 'td') { // DO SOMETHING WITH THE CELL } } 

В функции editCell мы подтверждаем, что целевой элемент является ячейкой таблицы, проверяя его имя тега. Эта проверка может быть слишком упрощена; Что если это другой элемент внутри ячейки таблицы, который является целью события? Может потребоваться быстрая модификация, которая добавляет код для поиска родительского элемента td . Что делать, если некоторые ячейки не должны быть редактируемыми? В этом случае мы можем добавить определенное имя класса в нередактируемую ячейку и проверить, что целевой элемент не имеет этого имени класса, прежде чем сделать его редактируемым. Доступно много вариантов, и вам просто нужно выбрать тот, который подходит вашему приложению.

Каковы плюсы и минусы?

Преимущества делегирования событий JavaScript:

  • Существует меньше обработчиков событий для установки и хранения в памяти . Это большой; лучшая производительность и меньше сбоев.
  • Нет необходимости повторно подключать обработчики после обновления DOM. Если содержимое вашей страницы генерируется динамически, например, с помощью Ajax, вам не нужно добавлять и удалять обработчики событий, поскольку элементы загружаются или выгружаются.

Потенциальные проблемы могут быть менее очевидными, но как только вы узнаете о них, их легко избежать:

  • Существует риск, что ваш код управления событиями может стать узким местом в производительности, поэтому держите его как можно меньше.
  • Не все события пузырьков. События blur , focus , load и unload не всплывают, как другие события. К событиям blur и focus можно получить доступ, используя фазу захвата (в браузерах, отличных от IE), а не фазу пузырьков, но это история для другого дня.
  • Вы должны быть осторожны при управлении событиями мыши. Если ваш код обрабатывает событие mousemove вы рискуете создать узкое место в производительности, потому что событие mousemove вызывается так часто. Событие mouseout имеет странное поведение, которым трудно управлять с делегированием события.

Резюме

Доступны примеры делегирования событий JavaScript, в которых используются основные библиотеки: jQuery , Prototype и Yahoo! Пользовательский интерфейс Вы также можете найти примеры, в которых вообще не используется библиотека, например, из блога Usable Type .

Делегирование событий — это удобный инструмент, который может быть в вашем наборе в случае необходимости и простой в реализации.