Eventy w JavaScript mogą być propagowane, co oznacza, że zdarzenie wywołane na elemencie potomnym przechodzi przez wszystkich rodziców. Zastosowanie wzorca Event Delegation polega na obsłudze zdarzenia w elemencie nadrzędnym. Jest przydatny, gdy mamy wiele podobnych elementów, a ich obsługa nie różni się w znacznym stopniu. To, co jest ważne w tym wzorcu, to propagacja zdarzeń oraz cel (target) zdarzenia. Poniżej przedstawiono kilka przykładów użycia.
Prosty przykład
Dobrym przykładem ilustrującym zastosowanie wzorca Event Delegation będzie menu, gdzie chcemy niestandardowo obsłużyć nawigację.
Struktura HTML będzie wyglądała mniej więcej tak:
<div id="menu">
<button data-page="home">Home</button>
<button data-page="about">About</button>
<button data-page="contact">Contact</button>
</div>
Code language: HTML, XML (xml)
Do obsługi nawigacji moglibyśmy tworzyć listenery na kliknięcia w poszczególne elementy button, ale dzięki wzorcowi Event Delegation obsłużymy kliknięcia w buttony za pomocą jednego listenera, który podepniemy pod div #menu
document.getElementById('menu').addEventListener('click', (event) => {
const page = event.target.dataset.page;
if (page) {
// obsługa nawigacji
console.log(`Navigate to ${navigation}`);
}
});
Code language: JavaScript (javascript)
Oczywiście, jeśli mielibyśmy więcej menu w różnych miejscach na stronie i obsługiwaliśmy je w ten sam sposób, nic nie stoi na przeszkodzie, abyśmy obsłużyli nawigację nasłuchując na elemencie
Określanie Celu (Target)
Weźmy teraz na tapetę bardziej skomplikowany przykład użycia wzorca Event Delegation. Będzie to podświetlenie wiersza w tabeli po kliknięciu, niezależnie czy klikniemy w komórkę czy w sam wiersz. Dodatkowo chcemy wykluczyć z tej logiki podświetlanie nagłówków tabeli.
HTML tabeli:
<table id="table">
<thead>
<tr>
<th>Kolumna 1</th>
<th>Kolumna 2</th>
<th>Kolumna 3</th>
<th>Kolumna 4</th>
</tr>
</thead>
<tbody>
<tr>
<td>Wiersz 1, Kolumna 1</td>
<td>Wiersz 1, Kolumna 2</td>
<td>Wiersz 1, Kolumna 3</td>
<td>Wiersz 1, Kolumna 4</td>
</tr>
....
</tbody>
</table>
Code language: HTML, XML (xml)
W JS będziemy nasłuchiwać na elemencie #table
document.getElementById('table').addEventListener('click', function(event) {
let row = event.target;
while (row.tagName !== this) {
if (row.tagName === 'TR') {
if (row.parentElement.tagName === 'TBODY') {
// obsługa podświetlenia wiersza
console.log('Highlight row', row);
}
return;
}
row = row.parentElement;
}
});
Code language: JavaScript (javascript)
Za pomocą pętli while sprawdzamy, czy zmienna row to element TR
. Jeśli tak, to sprawdzamy, czy row znajduje się w TBODY
. Jeśli oba warunki są spełnione, możemy podświetlić wiersz i zakończyć funkcję. Jeśli row znajdowałby się w THEAD
, wtedy nie obsłużymy podświetlenia i zakończymy funkcję.
W ten sposób możemy określić odpowiedni cel (target) i wykonać na nim potrzebne operacje.
Podsumowanie
Event Delegation jest skutecznym narzędziem, gdy musimy obsłużyć wiele elementów w ten sam sposób lub gdy elementy w drzewie DOM są usuwane lub dodawane, a my musimy reagować na ich zdarzenia. Jest wydajny, ponieważ zamiast nasłuchiwać wielu elementów, nasłuchujemy tylko na jednym.
Musimy pamiętać, że by ten wzorzec działał, zdarzenie musi być propagowane aż do elementu, w którym zawarta jest obsługa. Oznacza to, że nie możemy wcześniej wywołać metody event.stopPropagation()
. Czasami mogą być też potrzebne dodatkowe działania do ustalenia odpowiedniego celu (targetu).