变异观察者触发两次

Mutation observer fires twice

我有以下有效的代码,但如果我单击一个面板然后再次单击它,突变观察器会在第二次单击时触发两次。有谁知道这是什么原因以及如何阻止它?

我猜这是因为它在添加 class 时触发一次,在删除 class 时触发一次。但如果是这种情况,当您执行删除 class 时 classList.contains 不应该为真(如果您单击另一个面板可以看到 - 只有那个添加 class 事件火灾)

const activeClass = 'active';
const $panels = $('.panel');
let counter = 0;

$panels.on('click', e => {
  const $panel = $(e.currentTarget);
  $panels.removeClass(activeClass);
  $panel.addClass(activeClass);
});

$panels.each((index, panel) => {
  const observer = new MutationObserver(mutationsList => {
    for (let i = 0; i < mutationsList.length; i++) {
      if (mutationsList[i].attributeName === 'class' && mutationsList[i].target.classList.contains(activeClass)) {
        console.log(mutationsList[i].target.className, counter);
        counter++;
      }
    }
  });

  observer.observe(panel, {
    attributes: true,
    childList: false,
    subtree: false
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="panel panel--1">
panel
</div>
<div class="panel panel--2">
panel
</div>
<div class="panel panel--3">
panel
</div>

无论我们单击哪个面板,当有一个活动面板时,您的代码都会进行两次更改:

  • 记录 1:删除 class
  • 记录 2:添加 class

您观察到的同一元素点击行为的差异是由处理程序的 if 条件引起的。这可能令人惊讶,但它看到 active class 出现在第一个突变记录(用于删除)的元素上,因为 MutationObserver 的处理程序作为微任务运行 - 这意味着它在当前任务完成后运行- 意味着 addClass() 已经在同一元素上添加了 class。

作为解决方案,如果单击的元素已经处于活动状态,您可以跳过该工作:

$panels.on('click', e => {
  const $panel = $(e.currentTarget);
  if (!$panel.hasClass(activeClass)) {
    $panels.removeClass(activeClass);
    $panel.addClass(activeClass);
  }
});

Mutation Observer 是异步的。这意味着它会记录所有更改,并在将完成的更改的完整记录传递给 DOM.

后调用回调函数

点击一次后 当您再次点击时

记录

  1. class 移除
  2. class 添加

当前状态:Class added(调用回调时)

因此,class列表包含 returns true。

经过大量的调试,我发现对于class列表中的每个动作,都会在突变列表中添加一条新的突变记录。但是,突变记录的目标是元素的最终状态,而不是事件发生时元素的状态。

为了解决这个问题,我已经对目标的 class 名称进行了测试,看看它是否已经触发了突变(幸运的是我的元素具有唯一的 classes)以及是否不触发功能。

这可能不是最好的做法,但似乎解决了我的问题,所以如果有人有更好的方法,请随时 post 它

const activeClass = 'active';
const $panels = $('.panel');

$panels.on('click', e => {
  const $panel = $(e.currentTarget);
  $panel.removeClass(activeClass);
  $panel.addClass(activeClass);
});

$panels.each((index, panel) => {
  const observer = new MutationObserver(mutationsList => {
    const groupedMutations = [];
    for (let i = 0; i < mutationsList.length; i++) {
      if (mutationsList[i].attributeName === 'class' &&
        groupedMutations.indexOf(mutationsList[i].target.className) == -1 &&
        mutationsList[i].target.classList.contains(activeClass)) {
        groupedMutations.push(mutationsList[i].target.className);
        console.log(mutationsList[i].target.className, i)
      }
    }
  });

  observer.observe(panel, {
    attributes: true,
    childList: false,
    subtree: false
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="panel panel--1">
  panel
</div>
<div class="panel panel--2">
  panel
</div>
<div class="panel panel--3">
  panel
</div>