【问题标题】:Why does MutationObserver fire twice for childList but never for characterData?为什么 MutationObserver 为 childList 触发两次,但从未为 characterData 触发?
【发布时间】:2016-02-25 23:28:53
【问题描述】:

我有一个简单的MutationObserver 设置作为测试。 HTML 有一个 span,其文本内容每秒更新一次(div 用于消息):

<span class="tester"></span>
<div id="msg"></div>

MutationObserver 设置为监视.tester,并在观察到更改时将文本写入#msg div。同时,setInterval() 每秒运行一次以更改 .tester 中的文本:

var target = document.querySelector('.tester');

var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log(mutation.type);
    $('#msg').append(mutation.type+"<br/>")
    setTimeout(function() { $('#msg').text(''); }, 500);
  });    
});

var config = { attributes: true, childList: true, characterData: true };
observer.observe(target, config);

setInterval(function() {
  $('#msg').text('');
  $('.tester').text("update: "+Math.random())
}, 1000);

我希望此代码每秒打印一次 characterData 已更改。根据Mozilla's docs for MutationObserver,它说关于characterData:“如果要观察到目标数据的突变,则设置为true。”相反,我没有看到 characterData 突变,但 确实 每秒看到两个 childList 突变。

为什么我没有看到任何 characterData 突变,为什么我看到 两个 childList 突变?

这是CodePen 的工作示例。

【问题讨论】:

  • 我还没有验证这一点,但我认为jQuery().text()/.innerText 隐式删除现有文本节点并在其位置添加一个新节点,而不是更新现有文本节点。尝试手动更改target.firstChild.nodeValue,看看是否有不同的结果。
  • @JeremyBanks:就是这样,您应该发布一个答案来证明它(与修改现有文本节点的nodeValue 形成对比)。
  • @T.J.Crowder 我尝试更改代码:我确实停止获取 childList 事件,但没有开始获取 characterData 事件。 :/我现在没有时间弄清楚为什么不。如果有其他人可以发布答案。
  • @JeremyBanks:完成。 (你必须观察subtree。)

标签: javascript dom mutation-observers


【解决方案1】:

原因如Jeremy Banks said:当你使用jQuery的text()时,它会删除所有的文本节点,然后添加新的节点。这不是对字符数据的更改,而是对childList 的更改:删除那里的节点并用新节点替换它。

要查看字符数据的变化,您必须修改现有文本节点的nodeValue观察subtree的修改:

var target = document.querySelector('.tester');

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        console.log(mutation.type);
        $('#msg').append(mutation.type+"<br/>")
        setTimeout(function() { $('#msg').text(''); }, 500);
    });    
});

var config = {
    attributes: true,
    childList: true,
    characterData: true,
    subtree: true                   // <=== Change, added subtree
};
observer.observe(target, config);

var timer = setInterval(function() {
    $('#msg').text('');
    // Change --VVVVV modifying the existing child node
    $('.tester')[0].firstChild.nodeValue = "updated" + Math.random();
}, 1000);

// Stop after 10 seconds
setTimeout(function() {
    clearInterval(timer);
}, 10000);
<span class="tester">x</span><!-- Change, added a starting child node -->
<div id="msg"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

关于为什么会有 两个 childList 突变的问题,是的,我认为你是对的:他们正在移除孩子,然后添加一个新孩子。如果我们使用replaceChild method,我们只会看到一个突变:

var target = document.querySelector('.tester');

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        console.log(mutation.type);
        $('#msg').append(mutation.type+"<br/>")
        setTimeout(function() { $('#msg').text(''); }, 500);
    });    
});

var config = {
    attributes: true,
    childList: true,
    characterData: true,
    subtree: true                   // <=== Change, added subtree
};
observer.observe(target, config);

var timer = setInterval(function() {
    $('#msg').text('');
    // Change --VVVVV modifying the existing child node
    var text = document.createTextNode("updated" + Math.random());
    var parent = $('.tester')[0];
    parent.replaceChild(text, parent.firstChild);
}, 1000);

// Stop after 10 seconds
setTimeout(function() {
    clearInterval(timer);
}, 10000);
<span class="tester">x</span><!-- Change, added a starting child node -->
<div id="msg"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

【讨论】:

  • 我明白了,谢谢您提供的详细信息。我在观察到我无法控制的节点上的类似突变行为(使用用户脚本观看网站)后编写了示例,因此他们必须使用一种机制(如.text())来替换您描述的数据。
  • 仍然想知道为什么 observer 会为 childList 触发两次。是否一次用于删除现有数据,另一次用于添加新数据?
  • @mix:这是我的看法,是的。因为这是对 childList 的两个单独的突变。现在如果他们使用replaceChild method,我怀疑只有一个,但他们可能会清除然后设置。
  • @mix:是的,刚刚添加到上面。
  • 为了测试这一点,我forked the codepen 并将.tester 文本的值添加到消息中。出乎意料的是,它显示了两个观察者事件的新文本值。我期待文本的第一行是空白的,第二行有更新。
猜你喜欢
  • 1970-01-01
  • 2016-06-02
  • 2011-01-20
  • 1970-01-01
  • 2017-01-08
  • 1970-01-01
  • 2011-01-27
  • 2021-07-08
  • 1970-01-01
相关资源
最近更新 更多