Na czym polega event delegation?

event delegation

Event delegation to jeden z najpotężniejszych wzorców do obsługi zdarzeń. Mimo że może brzmieć groźnie, to wcale nie jest trudny w zrozumieniu. Wykorzystanie delegacji zdarzeń sprawi, że aplikacja będzie bardziej wydajna i uprości dynamiczne dodawanie i usuwanie elementów z DOM.

Event bubbling

Aby zrozumieć, czym jest event delegation, trzeba najpierw zrozumieć, w jaki sposób obsługiwane są zdarzenia. Po wywołaniu akcji generującej zdarzenie jest ono przekazywane w górę drzewa DOM, zaczynając od elementu, który zapoczątkował akcję, a kończąc na window (o ile wcześniej w sposób świadomy nie przerwiemy tego łańcucha za pomocą event.stopPropagation()).

W przypadku gdy element znajdujący się na tej ścieżce nasłuchuje na dane zdarzenie, zostaje wywołana funkcja przekazana do jego obsługi. Poniżej możesz zobaczyć event bubbling w akcji.

<div id="outer">
  <div id="inner">
    <button id="button">
      Click me!
    </button>
  </div>
</div>


document.getElementById('outer')
  .addEventListener('click', () => console.log('outer'));

document.getElementById('innter')
  .addEventListener('click', () => console.log('inner'));

const button = document.getElementById('button');
button.addEventListener('click', () => console.log('button'));
button.click();

// button
// inner
// outer

Event delegation

Jeśli wiesz, w jaki sposób obsługiwane są zdarzenia, to zrozumienie ich delegacji nie powinno sprawić problemów. Mając wiele elementów obsługujących zdarzenie w ten sam sposób, zamiast nasłuchiwać na to zdarzenie na każdym z nich, nasłuchujemy na ich wspólnym rodzicu.

Istotne jest to, że event.target zawsze zwróci nam element, który zapoczątkował zdarzenie.

<div id="parent">
  <button class="child">First</button>
  <button class="child">Second</button>
  <button class="child">
    T<span>hir</span>d
  </button>
</div>


// Without event delegation
document.querySelectorAll('.child')
  .forEach(node =>
    node.addEventListener('click', handleEvent)
)

// With event delegation
document.getElementById('parent')
  .addEventListener('click', handleEventOnParent);


function handleEvent(event) {
  console.log(event.target.textContent);
}

function handleEventOnParent(event) {
  const button = event.target.closest('button.child');

  if (!button) return;
  
  console.log(button.textContent)
}

Zauważ, że w funkcji handleEventOnParent jest dodatkowa logika mająca zagwarantować poprawne obsłużenie zdarzenia.

Po pierwsze za pomocą Element.closest(selector) szukamy, poruszając się w górę drzewa DOM, najbliższego elementu spełniającego przekazany selektor. Jeżeli nie uda się go znaleźć, zostanie zwrócony null.

Korzystamy z metody closest(), aby nawet w przypadku kliknięcia span znajdującego się wewnątrz przycisku, dostać się do odpowiedniego elementu i poprawnie obsłużyć zdarzenie.

Następnie sprawdzamy, czy zdarzenie wystąpiło poza przyciskiem, czyli czy closest() zwróciło null. Jeśli tak było, to kończymy wywołanie funkcji. W przeciwnym wypadku, mając dostęp do pożądanego elementu, obsługujemy zdarzenie.

Podsumowanie

Event delegation polega na wykorzystaniu mechnizmu propagacji zdarzeń w celu obsłużenia ich na wyższym poziomie DOM.

Jedną z największych zalet wykorzystania delegacji zdarzeń jest oszczędność pamięci przeglądarki, a co za tym idzie płynniejsze działanie aplikacji. Zamiast nasłuchiwać na zdarzenia na dziesiątkach elementów, nasłuchujemy tylko na jednym.

Drugą zaletą jest ułatwienie dynamicznego dodawania i usuwania elementów. Obsługa zdarzeń jest już zaimplementowana na kontenerze, więc odchodzi zajmowanie się listenerami na nowych elementach. A im mniej kodu, tym mniej miejsca na popełnienie błędu.

Jeśli chcesz poćwiczyć wykorzystanie event delegation, to Wes Bos przygotował bardzo ciekawe zadania w swoim darmowym kursie Javascript 30 (dzień 15 i 25).

Pobierz darmowy ebook zawierający 20 pytań, które możesz usłyszeć na rozmowie kwalifikacyjnej