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

喜夏-厌秋 提交于 2020-07-06 10:43:21

问题


I have a simple MutationObserver setup as a test. The HTML has a span whose text content gets updated once per second (and a div for messages):

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

The MutationObserver is set to watch .tester and writes text to the #msg div when it observes a change. Meanwhile, a setInterval() runs once/second to change the text in .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);

I would expect this code to print once per second that the characterData has changed. According to Mozilla's docs for MutationObserver, it says about characterData: "Set to true if mutations to target's data are to be observed." Instead, I see no characterData mutations but do see two childList mutations every second.

Why am I not seeing any characterData mutations, and why am I seeing two childList mutations?

Here's a working example with CodePen.


回答1:


The reason is as Jeremy Banks said: When you use jQuery's text(), it removes all the text nodes and then adds in new ones. That's not a change to character data, it's a change to the childList: Removing the node that's there and replacing it with a new one.

To see a change to character data, you have to modify the existing text node's nodeValue, and observe subtree modifications:

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>

Re your question about why there are two childList mutations, yes I think you're right: They're removing the child, then adding a new one. If we use the replaceChild method, we see only a single mutation:

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>


来源:https://stackoverflow.com/questions/35640501/why-does-mutationobserver-fire-twice-for-childlist-but-never-for-characterdata

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!