【问题标题】:MutationObserver not picking up on child nodesMutationObserver 没有在子节点上拾取
【发布时间】:2020-04-20 04:01:11
【问题描述】:

小提琴: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>

【问题讨论】:

  • 它工作正常。将其全部加载到控制台需要很长时间。做const domObserver = new MutationObserver(records=&gt;{ records.forEach(record=&gt;{ console.log(record.target.innerHTML); }); });

标签: javascript mutation-observers


【解决方案1】:

看起来当被观察的容器设置了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>

结果:

如果您想改用 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>

【讨论】:

  • FWIW TreeWalker API 可能是比递归更好的解决方案。或者一个简单的 getElementsByTagName('*') 以防只需要处理元素。
  • 您的代码运行良好,我非常感谢您的解释。谢谢!!
  • @somebodysomewhere 当某个答案解决了您的问题时,您可以考虑投票和/或将其标记为已接受(选中左侧的复选框)以表明问题已解决 :)
猜你喜欢
  • 2011-11-02
  • 1970-01-01
  • 1970-01-01
  • 2017-10-10
  • 1970-01-01
  • 2012-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多