MutationObserver не обнаруживает дочерние узлы - PullRequest
2 голосов
/ 20 апреля 2020

Fiddle: https://jsfiddle.net/7gj26hqu/

Я хотел бы иметь один MutationObserver, который может обнаружить все новые узлы внутри себя. В этом примере я установил {childList: true, subtree: true}, но div#nested не отображается в MutationObserver (отображается в консоли).

Как наблюдатель может обнаружить любую глубину дочерних узлов?

const domObserver = new MutationObserver((records) => {
  records.forEach((record) => {
    console.log(record)
  })
})

domObserver.observe(document.querySelector('#frame'), {childList: true, subtree: true})

// copy child nodes out of #template (as a string) and inject them into #frame for the observer to detect
document.querySelector('#frame').innerHTML = document.querySelector('#template').innerHTML
<div id="frame"></div>

<div id="template" style="display: none;">
   <div class="level-1">
    <div class="level-2">

      <div id="nested">
        I exist in the DOM but am not being seen by the MutationObserver
      </div>

    </div>
  </div>

  <div class="level-1">
    <div class="level-2">

    </div>
  </div>

  <div class="level-1">
    <div class="level-2">

    </div>
  </div>
</div>

1 Ответ

0 голосов
/ 20 апреля 2020

Похоже, когда наблюдаемый контейнер имеет свой набор innerHTML, дочерние элементы контейнера очищаются, а затем добавляются новые дочерние элементы полностью неповрежденными . Используя синхронного наблюдателя (просто чтобы вы могли видеть, что происходит), посмотрите, как существуют дочерние узлы при добавлении элемента:

// Look at results in browser console:

window.addEventListener('DOMNodeInserted', (e) => {
  console.log(e.path[0].childNodes.length);
});

document.querySelector('#frame').innerHTML = document.querySelector('#template').innerHTML
<div id="frame"></div>

<div id="template" style="display: none;">
   <div class="level-1">
    <div class="level-2">
    
      <div id="nested">
        I exist in the DOM but am not being seen by the MutationObserver
      </div>
      
    </div>
  </div>
  
  <div class="level-1">
    <div class="level-2">
    
    </div>
  </div>
  
  <div class="level-1">
    <div class="level-2">
    
    </div>
  </div>
</div>

Внук наблюдаемого контейнера прикрепляется к дочернему элементу до того, как дочерний элемент прикрепляется к контейнеру, поэтому subtree: true не видит действие вложения.

Чтобы обнаружить все дочерние узлы, вставленные таким образом, вам придется рекурсивно перебирать все элементы в MutationRecord вручную, несмотря на subtree: true.

const recurse = (parent) => {
  
  console.log(parent);
  if (parent.childNodes) {
    [...parent.childNodes].forEach(recurse);
  }
};
const domObserver = new MutationObserver((records) => {
  for (const record of records) {
    for (const node of record.addedNodes) {
      recurse(node);
    }
  }
})

domObserver.observe(document.querySelector('#frame'), {childList: true, subtree: true})

// copy child nodes out of #template (as a string) and inject them into #frame for the observer to detect
document.querySelector('#frame').innerHTML = document.querySelector('#template').innerHTML
<div id="frame"></div>

<div id="template" style="display: none;">
   <div class="level-1">
    <div class="level-2">

      <div id="nested">
        I exist in the DOM but am not being seen by the MutationObserver
      </div>

    </div>
  </div>

  <div class="level-1">
    <div class="level-2">

    </div>
  </div>

  <div class="level-1">
    <div class="level-2">

    </div>
  </div>
</div>

Результат:

enter image description here

Если вы хотите использовать TreeWalker вместо:

const frame = document.querySelector('#frame');
const domObserver = new MutationObserver(() => {
  // If the container's innerHTML was assigned to, iterate over all descendants:
  const treeWalker = document.createTreeWalker(frame);
  const nodes = [];
  let currentNode = treeWalker.currentNode;
  while (currentNode) {
    nodes.push(currentNode);
    currentNode = treeWalker.nextNode();
  }
  console.log(nodes);
});

domObserver.observe(frame, {
  childList: true,
  subtree: true
})

// copy child nodes out of #template (as a string) and inject them into #frame for the observer to detect
document.querySelector('#frame').innerHTML = document.querySelector('#template').innerHTML
<div id="frame"></div>

<div id="template" style="display: none;">
  <div class="level-1">
    <div class="level-2">

      <div id="nested">
        I exist in the DOM but am not being seen by the MutationObserver
      </div>

    </div>
  </div>

  <div class="level-1">
    <div class="level-2">

    </div>
  </div>

  <div class="level-1">
    <div class="level-2">

    </div>
  </div>
</div>
...