Не повторяйтесь.

СУХОЙ.

Это одна из самых распространенных мантр, которую повторяют начинающие программисты. Рефакторинг кода для минимизации повторений имеет много ощутимых преимуществ. Это делает код более легким для чтения, понимания и редактирования. Это сокращает время загрузки и создает более быстрые приложения. Что наиболее важно для студентов, это укрепляет концептуальное понимание и помогает части портфолио выделиться для потенциальных работодателей.

Делегирование событий часто является отличным способом начать рефакторинг кода JavaScript новичка. Это позволяет разместить один EventListener в родительском элементе, который выполняет работу нескольких, десятков или даже сотен EventListener. Это делает код настолько СУХИМ, что вы можете переехать в Моав и начать бизнес по производству наждачной бумаги.

В качестве примера я создал самую простую в мире социальную сеть. Ниже вы найдете HTML-код одного сообщения от анонимного пользователя, который съел вкусные тако на обед. Читатели могут отреагировать «Мне нравится», «Не нравится» или «Смеяться», нажав на одну из трех кнопок со смайликами, каждая из которых имеет свой собственный идентификатор. Все три кнопки находятся внутри контейнера div.

HTML

<!DOCTYPE html> 
<html lang="en"> 
<head>     
   <meta charset="UTF-8">     
   <meta http-equiv="X-UA-Compatible" content="IE=edge">     
   <meta name="viewport" content="width=device-width, initial-scale=1.0">     
   <title>Event Delegation</title>     
   <script src="index.js" defer></script> 
</head> 
<body>     
   <p class="post">I ate tacos for lunch today.</p>     
   <div id="button-container">         
      <button id="like-button">👍</button>         
      <button id="dislike-button">👎</button>         
      <button id="laugh-button">🤣</button>     
   </div> 
</body> 
</html>

Это отображается в DOM следующим образом:

Мы хотим, чтобы счетчик добавлял «+1» к каждой кнопке каждый раз, когда она нажимается. Для начала наиболее логичным решением может быть добавление EventListener к каждому элементу кнопки:

JavaScript (перед делегированием)

const $likeButton = document.querySelector('#like-button'); 
const $dislikeButton = document.querySelector('#dislike-button'); const $laughButton = document.querySelector('#laugh-button');  
let dislikesCounter = 0; 
let likesCounter = 0; 
let laughsCounter = 0;  
$likeButton.addEventListener('click', () => {
   $likeButton.innerText = `👍${likesCounter += 1}`; 
})  
$dislikeButton.addEventListener('click', () => {
   $dislikeButton.innerText = `👎${dislikesCounter += 1}`; 
})
$laughButton.addEventListener('click', () => {
   $laughButton.innerText = `🤣${laughsCounter += 1}`; 
})

Теперь, когда мы нажмем одну из кнопок, счетчик этой кнопки увеличится.

Эй, это работает! Но что, если мы можем выполнить ту же работу только с одним EventListener? В этом сила и красота делегирования событий.

(Примечание. Базовое понимание распространения событий — фазы захвата, целевой фазы и фазы всплывающей подсказки — полезно, хотя и не является строго обязательным. Я рекомендую этот блог Дмитрия Павлутин для более подробного объяснения того, что происходит под капотом.)

Вместо каждой отдельной кнопки давайте выберем div-контейнер кнопки и добавим EventListener выше в дереве DOM. Передача event в качестве аргумента позволяет получить доступ ко всем видам атрибутов через свойство event.target. Мы можем выбрать каждую кнопку на основе ее innerText, textContent, className, типа атрибута и т. д., связав их вместе с помощью синтаксиса, такого как event.target.className или event.target.textContent. Поскольку каждой кнопке в нашем примере уже назначен идентификатор, давайте воспользуемся им для их выбора. Console.log(event.target.id) показывает, что является целью и почему.

document.querySelector('#button-container').addEventListener('click', (event) => {
   console.log(event.target.id); 
})

Щелчок по каждой из кнопок вернет назначенный идентификатор кнопки из HTML-документа в консоли. Поскольку мы можем получить идентификаторы кнопок через этот EventListener более высокого уровня, все, что осталось сделать, — это перебрать их и добавить действия на основе возвращаемых значений event.target. Это та же логика, что и раньше, только использование цикла, операторов if/else или оператора switch.

JavaScript (с делегированием)

let likesCounter = 0; 
let dislikesCounter = 0; 
let laughsCounter = 0;  
document.querySelector('#button-container').addEventListener('click', (event) => {
   switch (event.target.id) {
      case 'like-button': 
         event.target.innerText = `👍${likesCounter += 1}`;
         break;
      case 'dislike-button':
         event.target.innerText = `👎${dislikesCounter += 1}`;
         break;
      case 'laugh-button':
         event.target.innerText = `🤣${laughsCounter += 1}`;
   } 
})

Это работает так же, как JavaScript перед делегированием, но с тремя строками кода меньше и одним EventListener вместо трех. В таком простом примере это может показаться не таким уж большим, но представьте, что вы используете эту концепцию для замены 100 EventListeners вместо горстки.

В заключение приведу практический пример. Для моего проекта Flatiron School Mod 3 я создал веб-сайт, который позволяет пользователям регистрировать свои восхождения на 14 000-футовые горы Колорадо. В указателе с 58 горными карточками (по одной для каждой вершины) пользователь может щелкнуть карточку, чтобы посетить страницу шоу для каждой вершины и оставить комментарий о недавних походных условиях. Сначала я выполнил это перенаправление, используя цикл forEach для массива горных объектов, чтобы добавить EventListener к каждой отдельной карте.

Однако, используя делегирование событий, я смог заменить эти 58 прослушивателей событий следующими пятью строками кода. Щелчок по каждой карточке — либо по окружающему пролету, либо по фактическому тексту H3 — переводит вошедшего в систему пользователя на выбранную страницу горного шоу с помощью параметров запроса и позволяет ему оставить комментарий.

document.querySelector('#summits-container').addEventListener('click', (event) => {
   if (event.target.tagName == "SPAN") {
         window.location.href = `/mountain.html?
         mountain=${event.target.id}&user_id=${id}`;     
   } 
})

Это делегирование событий! Это одна из самых крутых и полезных концепций, которые я выучил за 10 недель обучения в школе программирования. Удачного рефакторинга.