Poznajemy inny sposób na rozłączenie eventu – remove event listener. Do dzieła.

Nasz zmodyfikowany markup:

<!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>
    <button id="btn">Click me</button>
    <button id="abortBtn">Remove</button>
    <button id="removeListener">Remove listener</button>
    <script src="./evtsignal.js" defer></script>
</body>
</html>

Skrypt z lekcji poprzedniej:

let btn = document.querySelector("#btn");
let abortBtn = document.querySelector("#abortBtn");

let controller = new AbortController();

btn.addEventListener("click", function(e){
    alert("btn clicked");
}, {signal: controller.signal});

btn.addEventListener("click", function(e){
    alert("btn clicked - this will not be aborted");
});

abortBtn.addEventListener("click", function(e){
    alert("removing event listeners from btn");
    controller.abort();
    this.setAttribute("disabled", true); //e.target.setAttribute("disabled", true)
}, {once: true});

Rozłączymy sobie ten drugi listener:

let btn = document.querySelector("#btn");
let abortBtn = document.querySelector("#abortBtn");
let removeBtn = document.querySelector("#removeListener");

let controller = new AbortController();

btn.addEventListener("click", function(e){
    alert("btn clicked");
}, {signal: controller.signal});

function clickHandler(e){
    alert("btn clicked - this will not be aborted via signal");
};

btn.addEventListener("click", clickHandler);

abortBtn.addEventListener("click", function(e){
    alert("removing event listener from btn via signal");
    controller.abort();
    this.setAttribute("disabled", true); //e.target.setAttribute("disabled", true)
}, {once: true});

removeBtn.addEventListener("click", function(e){
    alert("removing event listener from btn via remove listener");
    btn.removeEventListener("click", clickHandler);
    e.target.setAttribute("disabled", true);
}, {once: true});

Ok, po pierwsze funkcja musi być nazwana. Po drugie – removeListener rozłączy, ale nawet ten sam event z tą samą funkcją, jeżeli jest zarejestrowany dwa razy, raz z caputre i raz bez to są dwa eventy.

I nie – nie wystarczy nazwać funkcji callback w addEventListener, żeby się ją dało odczepić przez remove. Ona musi być nazwana i zdefiniowana tak, aby removeEventListener miał ją w swoim zasięgu.

A co do capture – przykład z MDN jak dodajemy i usuwamy event:

element.addEventListener("mousedown", handleMouseDown, true);
element.removeEventListener("mousedown", handleMouseDown, true);

Można też podać opcje:

element.removeEventListener("mousedown", handleMouseDown, { capture: true });

To jest takie nieco głupie ułatwienie, bo w zasadzie dla removeEventListenera liczą się trzy rzeczy:

  • Rodzaj eventu
  • Funkcja
  • Opcja capture

Czyli ten sam event z tą samą funkcją ale podpięty dwa razy z różnym capture to są dwa eventy. Reszta nie ma znaczenia, ale jak ktoś chce tak rozłączać, też może.

Tym niemniej strasznie denerwujące jest to, że musimy wszystkie funkcje trzymać w zasięgu globalnym aby można je było rozłączyć. Porównajmy z AbortControllerem – wystarczy jeden kontroler do rozłączenia listenera, w dodatku nie musimy się martwić, on już znajdzie odpowiednią funkcję, wystarczy w opcjach podać signal.