Kolejna szybka i przyjemna lekcja dot. eventów w JavaScript, w których musimy stać się mistrzami jeżeli chcemy bawić się we frameworki frontendowe. Do dzieła.

Ok, markup do naszego ćwiczenia:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="myDiv">
        Text node in div
        <ul id="myUl">
            Text node in ul
            <li>Click Me</li>
            <li>Click Me</li>
            <li>Click Me</li>
        </ul>
    </div>
    <script src="./evtphase.js" defer></script>
</body>
</html>

JS do naszego ćwiczenia:

let div = document.querySelector("#myDiv");
let ul = document.querySelector("#myUl");
let liFirst = ul.querySelector("li");


liFirst.addEventListener("click", function(e){
    console.log(e.target);
    alert("li clicked");
});

ul.addEventListener("click", function(e){
    console.log(e.target);
    alert("ul clicked");
});

div.addEventListener("click", function(e){
    console.log(e.target);
    alert("div clicked");
});

Więc tak, po pierwsze znamy już fazy eventów (capture, target, bubble). Jak widzimy, event.currentTarget to jest element z eventListenerem, element dostępny pod this, target zaś to element, na którym dokonano eventu.

Targetem nie może być text node, text node liczy się jako swój rodzic HTMLowy (ale nie zawsze ten rodzic ma wysokość i szerokość, dlatego dałem do ułatwienia).

Jak widać eventy odpalają się w fazie bąbelkowania, nie w capture phase.

To, że klikając li odpalimy aż trzy eventy to mam nadzieję, że jest zupełnie jasne, natomiast zwracam uwagę na kolejność. Najpierw leci capture phase, potem target phase i odpalany jest event, następnie bąbelkujemy w górę i odpalamy kolejne.

Bąbelkowanie nazywane jest propagacją, zaś propagację możemy zatrzymać:

let div = document.querySelector("#myDiv");
let ul = document.querySelector("#myUl");
let liFirst = ul.querySelector("li");


liFirst.addEventListener("click", function(e){
    console.log(e.target);
    alert("li clicked");
    e.stopPropagation();
});

ul.addEventListener("click", function(e){
    console.log(e.target);
    alert("ul clicked");
    e.stopPropagation();
});

div.addEventListener("click", function(e){
    console.log(e.target);
    alert("div clicked");
    e.stopPropagation();
});

Dlaczego na końcu? A dlaczego nie? Ludziom, który ledwo ogarniają JS stopPropagation myli się często z preventDefault. Które robimy na początku, aby zapobiec defaultowemu działaniu eventu (częste przy formularzach oraz anchorach, które mają robić scrollintoview albo zapytać o coś).

Potem dopisuje się działanie. A z propagacją? O ile się nie mylę, to jest 100% synchroniczne. Czyli event „bąbel wyżej” nie ruszy zanim „bąbel niżej” nie skończy. Aż musiałem to sprawdzić, czy aby synchronicznie to nie działa, że z jednej strony event się odpala, z drugiej już propaguje. Chyba nie.

Aczkolwiek możemy na początku robić stopPropagation, w sumie to dobra praktyka. Ale aż mnie zastanowiło to i muszę koniecznie sprawdzić jak to działa.

Więcej eventów niedługo.