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.