Dalej drążymy temat, przy okazji poznając dlaczego delegacja eventów oraz mutation observery to narzędzia, z których warto korzystać.
Ok, nasz nowy 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>
I teraz dodamy obsługę formularza:
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 = "";
});
Już teraz widzimy, jak bardzo delegacja się przydaje. Nic nie musieliśmy podpinać, żadnych event listenerów do buttona, możemy dodawać i usuwać nowe rekordy.
Inne rzeczy wcześniej poznane też się przydają (jak insertAdjacentHTML).
Ok, teraz jak usuwamy mamy unknown mutation, bo tak sobie kod poprzednio napisaliśmy. Pora to zmienić:
const observer = new MutationObserver(callback);
function callback(mutationsList, observer) {
for (let mutation of mutationsList) {
switch(mutation.type) {
case 'childList':
let _target = mutation.target;
if(mutation.removedNodes.length){
console.log("you removed");
break;
} else if (mutation.addedNodes.length) {
console.log("you added")
break;
}
default:
console.log("unknown mutation");
}
}
}
observer.observe(ul, { childList: true});
Teraz możemy dopisać całą logikę:
const observer = new MutationObserver(callback);
function callback(mutationsList, observer) {
for (let mutation of mutationsList) {
switch(mutation.type) {
case 'childList':
let _target = mutation.target;
if(mutation.removedNodes.length){
if(
_target.childElementCount === 0
&&
_target.childNodes.length > 0)
{
_target.removeChild(_target.firstChild);
}
break;
} else if (mutation.addedNodes.length) {
if(_target.firstChild === _target.firstElementChild){
_target.insertAdjacentText("afterbegin", "Languages to learn: ");
}
break;
}
default:
console.log("unknown mutation");
}
}
}
observer.observe(ul, { childList: true});
Pełna reaktywność, super. A jak ten kod uważamy za bałaganiarski (w sumie dużo ifów), to znaczy, że koniecznie potrzebujemy frameworka frontendowego. Tym niemniej to są rzeczy (jak delegacja, obserwery), które pozwalają wycisnąć maksimum z czystego JS.
Ponadto ogarniając te tematy łatwiej nam:
- przyzwyczaić się do tego, co robi reaktywny framework frontendowy
- ogarnąć konceptualnie co i jak robi ten framework i jakie ma ograniczenia
- znając JS nie jesteśmy głąbami przy pierwszym lepszym problemie w frameworku JSowym do rozwiązania, którego „na tutorialu nie było”