如果我使用 setAttribute,MutationObserver 会挂起

MutationObserver hangs if I use setAttribute

我有一个观察者,它监听 listitem div 属性的任何变化,如果属性发生变化,它会将 div 的背景颜色设置为红色。

其实你可以很容易地自己测试一下。打开 Chrome 的书签管理器,在控制台中启动此脚本,然后单击任何书签或文件夹。它将被染成红色。

代码块中间提到的问题:如果我使用setAttribute,脚本将被挂起。

第一种方法(使用style.attributeName)不适合我,因为在我的实际代码中我使用自定义属性,而不是background-color

这个问题看起来很奇怪。有什么办法可以解决?

<div role="listitem"></div>
<div role="listitem"></div>
<div role="listitem"></div>

<script>
var target = document.querySelectorAll("[role='listitem']");
for (var i = 0; i < target.length; i++) {

    // create an observer instance
    var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {


            // The Problem
            // ===========

            // This work well
            mutation.target.style.backgroundColor = "red";

            // But this doesn't
            // mutation.target.setAttribute("style", "background-color: red;");

        });
    });

    // configuration of the observer
    var config = { attributes: true };

    // pass in the target node, as well as the observer options
    observer.observe(target[i], config);
}
</script>

您通过更改属性来响应每个属性更改。这会导致问题也就不足为奇了:您会让观察者再次开火。显然 setAttribute 会更新属性对象,即使您设置了相同的值(我认为这并非不合理,尽管有点令人惊讶)。

相反,我只会在它确实需要更改时设置它:

if (mutation.target.getAttribute("yourattr") != targetvalue) {
    mutation.target.setAttribute("yourattr", targetvalue);
}

我检查过将属性设置为相同的值 是否会再次触发观察者(在 Chrome 上):

var target = document.getElementById("target");
var counter = 0;
var observer = new MutationObserver(function() {
    ++counter;
    console.log(Date.now() + ": " + counter);
    if (counter < 10) {
        console.log("Inside handler: Setting data-foo to bar");
        target.setAttribute("data-foo", "bar");
    } else {
        console.log("Inside handler: Stopped, to avoid looping forever");
    }
});
observer.observe(target, {attributes: true});
console.log("Initial change: Setting data-something-else to testing");
target.setAttribute("data-something-else", "testing");
<div id="target"></div>

所以首先检查可以防止这种情况发生:

var target = document.getElementById("target");
var observer = new MutationObserver(function() {
    console.log(Date.now() + ": In handler");
    if (target.getAttribute("data-foo") != "bar") {
        console.log("Inside handler: Setting data-foo to bar");
        target.setAttribute("data-foo", "bar");
    } else {
        console.log("Inside handler: No need to set it, it's already set");
    }
});
observer.observe(target, {attributes: true});
console.log("Initial change: Setting data-something-else to testing");
target.setAttribute("data-something-else", "testing");
<div id="target"></div>