Прихований ключ до динамічних веб-взаємодій

Коли я ще вивчав основи веб-розробки, однією з несподіваних і інтригуючих дій, з якими я зіткнувся, було витікання подій. Спочатку це здавалося незвичайним, але коли ви подумаєте про витікання подій, ви побачите, що це має великий сенс. Як веб-розробник, ви обов’язково зіткнетеся з подіями. Отже, що таке подія?

Щоб надати користувачам можливість взаємодії з веб-сторінками, JavaScript покладається на події. Подія відноситься до випадків або дій, які можна виявити та відреагувати на написаний вами код. Приклади подій включають, серед іншого, клацання мишею, натискання клавіш і надсилання форми.

JavaScript використовує прослуховувачі подій для виявлення та відповіді на події. Слухач подій — це функція, яка прослуховує або чекає на певну подію на сторінці. Наприклад, подія натискання кнопки. Коли слухач події виявляє подію, яку він очікує, він відповідає виконанням коду, пов’язаного з подією. Весь цей процес захоплення та реагування на події називається обробкою подій.

А тепер уявіть, що на сторінці є три елементи: div, span і кнопка. Елемент кнопки вкладено в елемент span, а елемент span – у div. Ілюстрація цього показана нижче:

Якщо припустити, що кожен із цих елементів має прослуховувач подій, який прослуховує подію клацання та друкує на консолі, коли клацає, що відбувається, коли ви клацаєте кнопку?

Щоб перевірити це самостійно, створіть папку, а в папці створіть файл HTML під назвою index.html, файл CSS під назвою style.css і файл JavaScript під назвою app.js.

У файл HTML додайте такий код:

<!DOCTYPE html>
<html lang="en">

<head>
  <title>Event bubbling</title>
  <link rel="stylesheet" href="https://wilku.top/the-hidden-key-to-dynamic-web-interactions/style.css">
</head>

<body>
  <div>
    <span><button>Click Me!</button></span>
  </div>
  <script src="app.js"></script>
</body>

</html>

У файлі CSS додайте наведений нижче код, щоб стилізувати елемент div і span.

div {
  border: 2px solid black;
  background-color: orange;
  padding: 30px;
  width: 400px;
}

span {
  display: inline-block;
  background-color: cyan;
  height: 100px;
  width: 200px;
  margin: 10px;
  padding: 20px;
  border: 2px solid black;
}

У файл JavaScript додайте наступний код, який додає прослуховувачі подій до елементів div, span і button. Усі списки подій очікують події клацання.

const div = document.querySelector('div');
div.addEventListener('click', () => {
  console.log("You've clicked a div element")
})

const span = document.querySelector('span');
span.addEventListener('click', () => {
  console.log("You've clicked a span element")
})

const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log("You've clicked a button")
})

Тепер відкрийте файл HTML у браузері. Огляньте сторінку, потім натисніть кнопку на сторінці. Що ви помічаєте? Результат натискання кнопки показано нижче:

Натискання кнопки запускає обробник подій, який прослуховує подію натискання на кнопці. Однак слухачі подій для елементів span і div також запускаються. Ви можете запитати, чому це так.

Натискання на кнопку запускає прослуховувач подій, приєднаний до кнопки, який друкує на консолі. Однак, оскільки кнопка вкладена в елемент span, натискання кнопки означає, що технічно ми також клацаємо елемент span, а отже, запускається його слухач подій.

Оскільки елемент span також вкладено в елемент div, клацання елемента span також означає, що елемент div також клацнуто, і, як наслідок, також запускається його слухач подій. Це бурхлива подія.

Пулькання подій

Спливання подій — це процес, за допомогою якого подія, ініційована у вкладеному наборі елементів HTML, поширюється або випливає з самого внутрішнього елемента, де вона була ініційована, і переміщується вгору по дереву DOM до кореневого елемента, запускаючи всі слухачі подій, які прослуховують цю подію.

Слухачі подій запускаються в певному порядку, який відповідає тому, як подія з’являється або поширюється вгору по дереву DOM. Розглянемо показано нижче дерево DOM, яке представляє структуру HTML, використаного в цій статті.

Подія клацання з’являється в дереві DOM

Дерево DOM показує кнопку, вкладену в span, яка вкладена в div, яка вкладена в тіло, а тіло вкладене в елемент HTML. Оскільки елементи були вкладені один в одного, коли ви натискаєте на кнопку, подія клацання запускає прослуховувач подій, прикріплений до кнопки.

Однак, оскільки елементи є вкладеними, подія переміститься вгору по дереву DOM (вибухає) до елемента span, потім div, потім body і, нарешті, елемента HTML, запускаючи всі прослуховувачі подій, які очікують подію клацання в цьому порядок.

Ось чому виконується слухач подій, приєднаний до елементів span і div. Якби у нас були прослуховувачі подій, які прослуховували клацання на body та елементі HTML, вони б також були запущені.

Вузол DOM, де відбувається подія, називається цільовим. У нашому випадку, оскільки клацання відбувається на кнопці, елемент кнопки є метою події.

Як зупинити витікання подій

Щоб запобігти виникненню події в DOM, ми використовуємо метод під назвою stopPropagation(), який доступний для об’єкта події. Розглянемо наведений нижче зразок коду, який ми використали, щоб додати слухач подій до елемента кнопки.

const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log("You've clicked a button");
})

Код призводить до події, що з’являється в дереві DOM, коли користувач натискає кнопку. Щоб запобігти виникненню події, ми викликаємо метод stopPropagation(), як показано нижче:

const button = document.querySelector('button');
button.addEventListener('click', (e) => {
  console.log("You've clicked a button");
  e.stopPropagation();
})

Обробник події — це функція, яка виконується при натисканні кнопки. Прослуховувач подій автоматично передає об’єкт події обробнику події. У нашому випадку цей об’єкт події представлено ім’ям змінної e, яке передається як параметр в обробнику події.

Цей об’єкт події, e, містить інформацію про подію, а також надає нам доступ до різноманітних властивостей і методів, пов’язаних із подіями. Одним із таких методів є stopPropagation(), який використовується для запобігання появи подій. Виклик stopPropagation() у обробнику подій кнопки запобігає виникненню події в дереві DOM із елемента кнопки.

Результат натискання кнопки після додавання методу stopPropagation() показано нижче:

Ми використовуємо stopPropagation(), щоб запобігти виникненню події з елемента, для якого ми її використовуємо. Наприклад, якщо ми хочемо, щоб подія клацання переходила від елемента кнопки до елемента span, а не переходила далі вгору по дереву DOM, ми б використали stopPropagation() для прослуховувача подій span.

Зйомка подій

Зйомка подій є протилежністю витікання подій. Під час захоплення події подія просочується від крайнього зовнішнього елемента до цільового елемента, як показано нижче:

Подія клацання просочується до цільового елемента через запис події

Наприклад, у нашому випадку, коли ви натискаєте елемент кнопки, під час захоплення подій першими запускаються прослуховувачі подій в елементі div. Слухачі на елементі span слідують, і, нарешті, будуть запущені слухачі на цільовому елементі.

Проте спливаюча подія є способом за замовчуванням, у який події поширюються в моделі об’єктів документа (DOM). Щоб змінити поведінку за замовчуванням із витікання подій на захоплення подій, ми передаємо третій аргумент нашим слухачам подій, щоб встановити для захоплення подій значення true. Якщо ви не передаєте третій аргумент до прослуховувачів подій, для запису подій буде встановлено значення false.

Розглянемо слухач події нижче:

div.addEventListener('click', () => {
  console.log("You've clicked a div element")
})

Оскільки він не має третього аргументу, для захоплення встановлено значення false. Щоб встановити для захоплення значення true, ми передаємо третій аргумент, логічне значення true, яке встановлює для захоплення значення true.

div.addEventListener('click', () => {
  console.log("You've clicked a div element")
}, true)

Крім того, ви можете передати об’єкт, який встановлює значення true, як показано нижче:

div.addEventListener('click', () => {
  console.log("You've clicked a div element")
}, {capture: true})

Щоб перевірити захоплення подій, додайте третій аргумент у свій файл JavaScript до всіх прослуховувачів подій, як показано:

const div = document.querySelector('div');
div.addEventListener('click', () => {
  console.log("You've clicked a div element")
}, true)

const span = document.querySelector('span');
span.addEventListener('click', (e) => {
  console.log("You've clicked a span element")
}, true)

const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log("You've clicked a button");
}, true)

Тепер відкрийте браузер і натисніть на елемент кнопки. Ви маєте отримати такий вихід:

Зауважте, що на відміну від підсвічування подій, де спочатку друкувався вихід із кнопки, під час захоплення подій перший вивід надходить із зовнішнього елемента, div.

Витікання подій і захоплення подій є основними способами, за допомогою яких події поширюються в DOM. Однак для розповсюдження подій зазвичай використовується підсвічування подій.

Делегація заходу

Делегування подій — це шаблон проектування, де один прослуховувач подій приєднаний до загального батьківського елемента, наприклад елемента

    , замість того, щоб мати прослуховувачі подій для кожного з дочірніх елементів. Події на дочірніх елементах потім переходять до батьківського елемента, де вони обробляються обробником подій на батьківському елементі.

    Таким чином, у випадку, коли ми маємо батьківський елемент із дочірніми елементами всередині нього, ми додаємо лише слухач події до батьківського елемента. Цей обробник подій оброблятиме всі події в дочірніх елементах.

    Вам може бути цікаво, як батьківський елемент дізнається, який дочірній елемент було натиснуто. Як згадувалося раніше, слухач подій передає об’єкт події до обробника подій. Цей об’єкт події має методи та властивості, які пропонують інформацію про певну подію. Однією з властивостей об’єкта події є цільова властивість. Цільова властивість вказує на конкретний елемент HTML, де сталася подія.

    Наприклад, якщо у нас є невпорядкований список із елементами списку, і ми прикріплюємо слухач подій до елемента

      , коли подія відбувається в елементі списку, властивість target в об’єкті події вказуватиме на певний елемент списку, де подія сталося.

      Щоб побачити делегування події в дії, додайте такий HTML-код до наявного HTML-файлу:

      <ul>
          <li>Toyota</li>
          <li>Subaru</li>
          <li>Honda</li>
          <li>Hyundai</li>
          <li>Chevrolet</li>
          <li>Kia</li>
        </ul>

      Додайте наведений нижче код JavaScript, щоб використовувати делегування подій для використання одного прослухувача подій у батьківському елементі для прослуховування подій у дочірніх елементах:

      const ul = document.querySelector('ul');
      ul.addEventListener('click', (e) => {
        // target element
        targetElement = e.target
        // log out the content of the target element
        console.log(targetElement.textContent)
      })

      Тепер відкрийте браузер і клацніть будь-який елемент у списку. Вміст елемента слід вивести на консоль, як показано нижче:

      Використовуючи єдиний слухач подій, ми можемо обробляти події в усіх дочірніх елементах. Наявність такої кількості прослуховувачів подій на сторінці впливає на її продуктивність, споживає більше пам’яті та призводить до повільного завантаження та відтворення сторінок.

      Делегування подій дозволяє нам уникнути всього цього, мінімізуючи кількість слухачів подій, які нам потрібно використовувати на сторінці. Делегування подій залежить від подій. Таким чином, ми можемо сказати, що підсвічування подій може допомогти оптимізувати продуктивність веб-сторінок.

      Поради щодо ефективної обробки подій

      Як розробник, працюючи з подіями в об’єктній моделі документа, подумайте про використання делегування подій замість того, щоб мати багато прослуховувачів подій для елементів на вашій сторінці.

      Використовуючи делегування подій, не забувайте приєднувати слухач подій до найближчого спільного предка дочірніх елементів, які потребують обробки подій. Це допомагає оптимізувати витікання подій, а також мінімізує шлях, який має пройти подія до її обробки.

      При обробці подій використовуйте об’єкт події, наданий слухачем подій, для вашої переваги. Об’єкт події містить такі властивості, як target, які стають у нагоді під час обробки подій.

      Щоб мати більш продуктивні веб-сайти, уникайте надмірних маніпуляцій DOM. Наявність подій, які викликають часті маніпуляції DOM, може негативно вплинути на ефективність вашого веб-сайту.

      Нарешті, у випадку вкладених елементів будьте дуже обережні, приєднуючи до елементів вкладені слухачі подій. Це може не тільки вплинути на продуктивність, але й ускладнить обробку подій, і ваш код буде важко підтримувати.

      Висновок

      Події є потужним інструментом у JavaScript. Спливання подій, захоплення подій і делегування подій є важливими інструментами обробки подій у JavaScript. Як веб-розробник, скористайтеся цією статтею, щоб ознайомитися з концепціями, щоб ви могли створювати більш інтерактивні, динамічні та продуктивні веб-сайти та програми.