为什么 MutationObserver 会为 childList 触发两次而不会为 characterData 触发?

Why does MutationObserver fire twice for childList but never for characterData?

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

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

MutationObserver 设置为观察 .tester 并在观察到变化时将文本写入 #msg div。同时,setInterval() 运行 once/second 来更改 .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:"Set to true if mutations to target's data are to be observed." 相反,我没有看到 characterData 突变,但是 do 每秒看到两个 childList 突变。

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

这是 CodePen 的工作示例。

原因如:当你使用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 突变的问题,是的,我认为你是对的:他们正在移除 child,然后添加一个新的。如果我们使用 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>