To co poznamy teoretycznie nie jest delegacją eventów, bo nie wykorzystuje mechanizmu przypisania eventu do rodzica i delegowaniu go do potomków, w praktyce wykorzystuje bardzo podobny mechanizm.
Ok, przypomnijmy sobie delegację eventów. Najpierw markup:
<ul id="list">
Languages to learn:
<li>C# <button class="btn-del">X</button></li>
<li>Java <button class="btn-del">X</button></li>
<li>Scala <button class="btn-del">X</button></li>
<li>Perl <button class="btn-del">X</button></li>
<li>Ruby <button class="btn-del">X</button></li>
</ul>
<form id="addForm">
<label for="language">Language: </label>
<input type="text" name="language" id="ipt">
<input type="submit" value="Add language">
</form>
No a teraz JS:
let ul = document.querySelector("#list");
let form = document.querySelector("#addForm");
let ipt = form.querySelector("#ipt");
ul.addEventListener("click", function(e){
if(!e.target.matches('button.btn-del'))
return;
e.target.closest("li").remove();
});
form.addEventListener("submit", function(e){
e.preventDefault();
ul.insertAdjacentHTML('beforeend',
`<li>${ipt.value}<button class="btn-del">X</button></li>`
);
ipt.value = "";
});
Tak się delegowało eventy. No ok, fajnie, teraz przypomnijmy sobie markup z lekcji poprzedniej:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./reactivevar.js"></script>
</head>
<body>
<div id="counter">
<reactive-variable
id="reactiveVar"
pref="Counter: "
value="0"> </reactive-variable>
<button id="incBtn">+</button>
<button id="decBtn">-</button>
<button id="resBtn">Reset</button>
</div>
<script src="./reactive2.js" defer></script>
</body>
</html>
Ten komponent był w jeszcze poprzedniej. A skrypt na dole body wygląda tak:
let cnt = {
value: 0,
id: 'reactiveVar'
};
const cntHandler = {
set(obj, prop, value){
if(prop !== 'value')
return Reflect.set(...arguments);
document.getElementById(obj.id)
.setAttribute('value', value);
return Reflect.set(...arguments);
}
}
let cntProxy = new Proxy(cnt, cntHandler);
console.log(cntProxy);
cntProxy.value++;
console.log(cntProxy);
let incBtn = document.querySelector("#incBtn");
incBtn.addEventListener("click", function(e){
cntProxy.value++;
});
I teraz logika jest podobna do delegacji. Co prawda nie chcemy nasłuchiwać na klik na counterze i delegować event do jego dzieci, które w zależności od tego, co zostało kliknięte odpowiadają w jakiś sposób.
Chcemy poprzypinać event to tych buttonów. Ale – raz napisać taki jeden event, który patrzy jaki button go aktywował i podjąć taką czy inną akcję w związku z tym.
I raz te buttony zaciągnąć i w pętli do nich przypisać. Myślę, że jasne, więc:
let buttons = document.querySelectorAll("#counter button");
buttons.forEach(function(btn){
btn.addEventListener("click", function(e){
if(e.target.matches("#incBtn"))
return cntProxy.value++;
if(e.target.matches("#decBtn"))
return cntProxy.value--;
if(e.target.matches("#resBtn"))
return cntProxy.value = 0;
});
});
I tak to wygląda, jest to ogólnie dość znany wzorzec. Niby nie jest to delegacja typu przypisz listener dla rodzica i niech on będzie działał tak a tak w zależności od tego jakie dziecko go wywoła.
Tu mamy dopisanie tego samego listenera i tego samego callbacku, który sam wnioskuje co ma robić. Rzecz często spotykana w JavaScript.