Делегирование событий в JavaScript

Делегирование событий в JavaScript

Здравствуйте! Всплытие событий, которое мы с вами рассматривали в прошлом уроке  позволяет реализовать один из самых я бы сказал  важных приёмов разработки – делегирование.

Делегирование заключается в том, что если у вас есть очень много элементов, события на которых нужно обрабатывать одним и тем же  образом, то вместо того, чтобы назначать обработчик каждому –  вы можете установить один обработчик на их общего родителя. Из него вы  можете получить  элемент event.target, чтобы понять на каком именно потомке произошло событие и соответственно  обработать его.

Делегирование событий JavaScript

 

Пример делегирование в таблице

Рассмотрим пример – это таблица.

Вот Её HTML (схематично):

<table>
  <tr>
    <th colspan="3"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>
  </tr>
  <tr>
    <td>...<strong>Northwest</strong>...</td>
    <td>...</td>
    <td>...</td>
  </tr>
  <tr>...еще 2 строки такого же вида...</tr>
  <tr>...еще 2 строки такого же вида...</tr>
</table>

В этой таблице всего 9 ячеек, но в принципе может  быть и 99, и даже больше не важно.

А вот наша задача – реализовать  допустим подсветку ячейки таблицы при клике.

Вместо того, чтобы назначать обработчик для каждой ячейки, это будет большой перерасход памяти, да к тому же когда вы будете добавлять новые ячейки,  то придется и обработчики добавлять тоже мы можем  создать единый обработчик на родительский  элемент <table>.

Он будет использовать  объект event.target, чтобы  таким образом получить элемент, на котором и произошло событие, и подсветить его.

Код может быть  таким:

var selected;

table.onclick = function(event) {
  var targ = event.target; // где был клик?

  if (targ.tagName != 'TD') return; // не на TD? тогда не надо

  highlight(targ); // подсветить TD
};

function highlight(node) {
  if (selected) {
    selected.classList.remove('highlight');
  }
  selected = node;
  selected.classList.add('highlight');
}

Такому коду совершенно безразлично, сколько ячеек у вас в таблице. Обработчик то всё равно один. Вы можете добавлять, удалять <td> из таблицы, менять их количество – ваша подсветка будет стабильно работать, поскольку обработчик стоит на <table>.

Читайте также  Объект события Event

Но у данной версии кода есть один недостаток.

Дело в том, что Клик может быть не на теге, который нас интересует, а внутри него.

В этом случае, если посмотреть на HTML таблицы внимательно,  вы можете заметить,  что ячейка содержит вложенные теги, например <strong>:

<td>
  <strong>Northwest</strong>
  ...Metal..Silver..Elders...
</td>

Конечно же клик может произойти внутри <td>, на элементе <strong>. Такой клик будет пойман обработчиком, но вот target у него будет не <td>, а <strong>:
Внутри обработчика table.onclick надо по event.target разобраться, в каком собственно  <td> был клик.

Для этого мы, используя ссылку parentNode, будем идти вверх по дереву  от event.target и выше и проверять:

  • Если нашли <td>, значит это то что нужно.
  • Если дошли до элемента table и при этом <td> не найден, то наверное клик был вне <td>, например на  заголовке таблицы.

Вот представлена улучшенная версия  table.onclick с циклом while, который это все и  делает:

table.onclick = function(event) {
  var target = event.target;
  // цикл двигается вверх от target к родителям до table
  while (target != table) {
    if (target.tagName == 'TD') {
      // нашли элемент, который нас интересует!
      highlight(target);
      return;
    }
    target = target.parentNode;
  }

  // возможна ситуация, когда клик был вне <td>
  // если цикл дошёл до table и ничего не нашёл,
  // то обработчик просто заканчивает работу
}

Можно также для этого использовать и метод closest, при поддержке браузером:

table.onclick = function(event) {
  var target = event.target;

  var td = target.closest('td');
  if (!td) return; // клик вне <td>, не интересует

  // если клик на td, но вне этой таблицы (возможно при вложенных таблицах)
  // то не интересует
  if (!table.contains(td)) return;

  // нашли элемент, который нас интересует!
  highlight(td);
}

Итого

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

Читайте также  Отмена действий браузера по умолчанию

Порядок выполнения:

  1. Вешаем обработчик на  родительский контейнер.
  2. В обработчике: получаем объект event.target.
  3. В обработчике: если event.target или один из его родителей в контейнере (this) – интересующий нас элемент –  то обработать его.

Зачем использовать:

  • Упрощает код и экономит память: не требуется вешать много обработчиков.
  • Меньше кода: при добавлении и удалении элементов не нужно устанавливать или удалять обработчики .
  • Удобство изменений: можно массово добавлять или удалять элементы путём простого изменения innerHTML.

Ограничения делегирования

  • Событие должно всплывать. Не допускается, чтобы промежуточный обработчик вызвал event.stopPropagation() до того, как событие доплывёт до нужного элемента.
  • Делегирование может  создавать дополнительную нагрузку на браузер, ведь обработчик запускается, когда событие происходит в любом месте контейнера, не обязательно на элементах, которые нам интересны.  Но как правило  эта нагрузка настолько мизерная, что её даже не стоит принимать во внимание.

Задачи

Скрытие сообщения с помощью делегирования

Пусть есть список сообщений.  Вам надо добавить каждому сообщению кнопку для его удаления.

Воспользуйтесь  делегированием  событий.  Один обработчик для всего.

Раскрывающееся дерево

Вам надо создать  дерево, которое по клику на заголовок скрывает-показывает детей:

Требования:

  • Используйте делегирование.
  • Клик вне текста заголовка ничего делать не должен.
  • При наведении на заголовок – он становится жирным, реализовать через стили.

 

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Плюсануть
Поделиться

Об авторе

admin administrator

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: