【问题标题】:Mutation Observer only triggering on Parent Element突变观察者仅在父元素上触发
【发布时间】:2020-03-23 18:15:51
【问题描述】:

我正在尝试使用 MutationObserver 来监视应用程序库中已删除的节点,但似乎在删除(或替换)父节点时,它不会触发所有子元素的回调。

<button class="RemoveListButton">Remove List</button>
<div class="Container">
  <ul class="ShoppingList">
    <li class="Item">Pasta</li>
    <li class="Item">Chips</li>
    <li class="Item">Salsa</li>
  </ul>
</div>
const removeWatcher = new MutationObserver(mutationList => {
    const removedNodes = mutationList.flatMap(m => [...m.removedNodes])
    console.log('Removing', removedNodes)
})

const container = document.querySelector('.Container')
removeWatcher.observe(container, { subtree: true, childList: true })

/* additional javascript to remove elements on click */

见 JSFiddle https://jsfiddle.net/eq4ahsvc/3/

在示例中,当我删除单个列表项(通过单击它)时,我在控制台中看到该元素已被 Mutation Observer 删除并看到。

但是,当我删除整个列表时,只会为父元素触发 Mutation Observer 回调,而不是为单个子元素触发。

有没有办法配置这种行为?理想情况下,每个元素都会触发 MutationObserver 回调,这样在删除列表时,我会看到删除列表的触发器,以及删除每个子元素的触发器。

【问题讨论】:

  • 如果一个有 1000 个子元素的元素像 el.remove() 一样被删除,它的 1000 个子元素不会被 MutationObserver 报告,这是性能的预期行为。你可以自己枚举元素。

标签: javascript dom mutation-observers


【解决方案1】:

子元素没有从其父元素中移除,因此没有可观察的移除事件。见:

const ul = document.querySelector('ul');
const li = ul.children[0];
ul.remove();

console.log(li.parentElement);
<button class="RemoveListButton">Remove List</button>
<div class="Container">
  <ul class="ShoppingList">
    <li class="Item">Pasta</li>
    <li class="Item">Chips</li>
    <li class="Item">Salsa</li>
  </ul>
</div>

如您所见,&lt;li&gt;s 仍然有 .ShoppingList 的父元素,因为 .ShoppingList 已从其容器中删除,但单个 &lt;li&gt;s 并未从 .ShoppingList 中删除.

如果您想查看每个已删除 &lt;li&gt; 的事件,除了删除 .ShoppingList 之外,您还必须迭代并显式删除它们:

const removeWatcher = new MutationObserver(mutationList => {
  const removedNodes = mutationList.flatMap(m => [...m.removedNodes])
  console.log('Removing', removedNodes)
})

const container = document.querySelector('.Container')
removeWatcher.observe(container, { subtree: true, childList: true })

const button = document.querySelector('.RemoveListButton')
button.onclick = () => {
  document.querySelectorAll('.ShoppingList > li').forEach(li => li.remove());
	document.querySelector('.ShoppingList').remove()
}

const listItems = document.querySelectorAll('.Item')
listItems.forEach(item => item.onclick = () => {
	event.target.remove()
})
<button class="RemoveListButton">Remove List</button>
<div class="Container">
  <ul class="ShoppingList">
    <li class="Item">Pasta</li>
    <li class="Item">Chips</li>
    <li class="Item">Salsa</li>
  </ul>
</div>

对于您想要的,要么这样做,要么递归地展平观察者回调中 removedNodes 数组中每个项目的所有子项。

【讨论】:

  • 哦,哇,这很有道理...我没有想到这些元素仍然附加到它们的父级,所以从技术上讲它们没有被删除...好吧,这个答案使感觉。谢谢!我可能会递归地压平所有孩子并在那里触发删除。
猜你喜欢
  • 2021-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-20
相关资源
最近更新 更多