Delegacja i propagacja zdarzeń w JavaScript

1. Propagacja zdarzeń
Propagacja zdarzeń to sposób, w jaki zdarzenia poruszają się w drzewie DOM. Wyróżniamy dwa kierunki propagacji:
- Faza capturing (przechwytywanie, w dół) – zdarzenie przechodzi od elementu
document
w dół do elementu, który je wywołał. - Faza bubbling (bąbelkowanie, w górę) – zdarzenie przechodzi od elementu, który je wywołał, do góry w drzewie DOM aż do
document
.
Przykład propagacji zdarzeń
Załóżmy, że mamy następujący HTML:
<div id="parent">
<button id="child">Click me</button>
</div>
I dodajemy dwa nasłuchiwacze:
document.getElementById("parent").addEventListener("click", () => {
console.log("PARENT clicked");
});
document.getElementById("child").addEventListener("click", () => {
console.log("CHILD clicked");
});
Jeśli klikniesz #child
, zobaczysz w konsoli:
CHILD clicked!
PARENT clicked!
Dzieje się tak, bo zdarzenie bąbelkuje (przechodzi od dziecka do rodzica).
Zatrzymanie propagacji zdarzeń
Jeśli nie chcemy, aby zdarzenie bąbelkowało w górę, możemy użyć event.stopPropagation()
:
document.getElementById("child").addEventListener("click", event => {
event.stopPropagation(); // Zatrzymuje propagację
console.log("CHILD clicked");
});
Teraz po kliknięciu #child
konsola wyświetli tylko: CHILD clicked , #parent
nie otrzyma zdarzenia.
Wymuszenie przechwytywania zdarzeń
Domyślnie nasłuchiwanie zdarzeń odbywa się w fazie bąbelkowania, co zonacza, że najpierw wywołane zostanie zdarzenie dla #child
, a następnie dla #parent
. Można jednak odwrócić tą kolejność, przypisując zdarzenie do do fazy przechwytywania, poprzez przekazanie true
jako trzeciego argumentu addEventListener
:
document.getElementById("parent").addEventListener("click", () => {
console.log("PARENT clicked (capturing)");
}, true);
Teraz #parent
zostanie uruchomiony przed #child
, ponieważ obsługuje zdarzenia w fazie przechwytywania.
2. Delegacja zdarzeń
Delegacja zdarzeń to technika polegająca na przypisywaniu event listenera do elementu nadrzędnego zamiast do każdego potomka osobno. Przydatne, gdy elementy dynamicznie pojawiają się w DOM (np. za pomocą ajax).
document.getElementById("parent").addEventListener("click", event => {
if (event.target.classList.contains("child")) {
console.log("Child clicked: ", event.target);
}
});
Jeśli później dodamy nowy element:
const newChild = document.createElement("div");
newChild.classList.add("child");
newChild.textContent = "Nowy element";
document.getElementById("parent").appendChild(newChild);
Nadal będziemy mogli wykryć kliknięcia w nowy element, mimo że nie dodaliśmy mu osobnego addEventListener
.