Kontynuacja lekcji poprzedniej, piszemy już coraz lepszy i coraz bardziej reaktywny czysty JavaScript, niedługo całą dokumentację MDN będziemy cytować z pamięci. Do dzieła!

Ok, poprzedni 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>

I poprzedni JS z delegacją eventów:

let ul = document.querySelector("#list");

ul.addEventListener("click", function(e){
   
    if(!e.target.matches('button.btn-del'))
        return;

    e.target.closest("li").remove();

    if(e.currentTarget.childElementCount === 0)
        e.currentTarget.removeChild(e.currentTarget.firstChild);
    
});

Teraz postaramy się ogarnąć zmianę dzieci-elementów za pomocą mutation observera:

const observer = new MutationObserver(callback);

function callback(mutationsList, observer) {
  for (let mutation of mutationsList) {
    if (mutation.type === "childList") {
      console.log("A child node has been added or removed.");
      console.log(mutation.target);
    } 
  }
}

observer.observe(ul, { childList: true});

Działa? Działa. Z naszą znajomością dokumentacji i mając target, który jest elementem i węzłem już dalej sobie poradzimy:

const observer = new MutationObserver(callback);

function callback(mutationsList, observer) {
  for (let mutation of mutationsList) {
    if (mutation.type === "childList") {
      let _target = mutation.target;
      if(_target.childElementCount === 0){
        _target.removeChild(_target.firstChild);
      }
    } 
  }
}

observer.observe(ul, { childList: true});

Ok, mamy error – dlaczego? Cóż, mutacja zostaje dokonana, warunek zostaje spełniony, zaś firstChild już nie istnieje (po usunięciu go). Możemy to naprawić:

const observer = new MutationObserver(callback);

function callback(mutationsList, observer) {
  for (let mutation of mutationsList) {
    if (mutation.type === "childList") {
      let _target = mutation.target;
      if(
        _target.childElementCount === 0
        &&
        _target.childNodes.length > 0
    ){
        _target.removeChild(_target.firstChild);
      }
    } 
  }
}

observer.observe(ul, { childList: true});

Możemy ten kod jeszcze ulepszyć:

const observer = new MutationObserver(callback);

function callback(mutationsList, observer) {

  for (let mutation of mutationsList) {

    switch(mutation.type) {

      case 'childList':

      let _target = mutation.target;
        if(
            _target.childElementCount === 0
            &&
            _target.childNodes.length > 0
        ){
            _target.removeChild(_target.firstChild);
            break;
        }


      default:
        console.log("unknown mutation");
    } 
  }
}

observer.observe(ul, { childList: true});

Od razu wygląda ładniej. Więcej w następnych lekcjach.