重新连接并断开突变观察者

Reconnect and disconnect a MutationObserver

本文关键字:观察者 突变 断开 重新连接      更新时间:2023-09-26

这个问题是这个问题的续集。但是,没有必要阅读上一篇,我只是为感兴趣的读者提供一个链接。

有一个观察器,它将对每个元素做出一些类的反应,如@Shomz建议的那样:

var target = document.querySelectorAll(".someclass");
for (var i = 0; i < target.length; i++) {
    create(target[i]);
}
function create(t) {
    var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            var foo = t.getAttribute("aaa")
            if (foo == "vvv")
                t.style.backgroundColor = "red";
        });
    });
    var config = {
        attributes: true
    };
    observer.observe(t, config);
}

因此,有两个紧密交织的问题。

1( 由于某些原因,观察者可能会断开连接。如何重新连接?我尝试使用observer.observe,但它在这里不起作用。

2(第二个问题,手动断开观察器连接的方法是什么?我尝试使用observer.disconnect();,但它也不起作用。

实际上,

您不必使用多个实例来观察多个 DOM 节点元素.
您可以使用一个突变观察器来观察多个 DOM 节点元素.
要在断开连接后重新连接观察器,您不必重新创建突变观察器的新实例,您只需再次在已创建的实例上调用 observe 方法,但只能在断开连接后。

断开连接((

阻止 MutationObserver 实例接收 DOM 突变的通知。在再次使用 observe() 方法之前,不会调用观察者的回调。

对已观察到的元素调用 observe(( 方法不会对观察产生任何影响。至少在对观察使用相同的观察者实例时。

注意:向元素添加观察器就像 addEventListener 一样,如果您多次观察该元素,它不会产生任何影响。这意味着如果你观察元素两次,观察回调不会触发两次,也不必运行两次 disconnect((。换句话说,一旦观察到一个元素,使用相同的观察者实例再次观察它将无济于事。但是,如果回调对象不同,它当然会向其添加另一个观察者。

下面是一个使用观察器实例观察几个图像元素的宽度属性的示例。该示例使用超时为每个图像宽度属性设置随机值。回调函数将输出更改并断开观察器的连接,然后重新开始整个过程。

var imgs = Array.prototype.slice.call( document.images ),
    config = { attributes: true, attributeOldValue: true },
    observer = new MutationObserver( mutationCallback );
function mutationCallback ( mutations ) {
  mutations.forEach(function( record ) {
    record.target.previousElementSibling.textContent = "";
    record.target.previousElementSibling.textContent = "The image "
      + record.attributeName 
      + " attribute changed from " 
      + record.oldValue 
      + " to " 
      + record.target.getAttribute('width')
      + ".";
  })
  observer.disconnect();
  startObserving( imgs );
}
function changeNodeAttr ( attr, nodes ) {
  window.setTimeout(function() {
    nodes.forEach(function( node ) {
      node.setAttribute( attr, Math.floor( Math.random()*( 300 - 100 + 1 ) +100 ) );
    })
  }, 2500)
}
function startObserving ( nodes ) {
  nodes.forEach(function( node ) {
    observer.observe( node, config );
  })
  changeNodeAttr( "width", imgs );
}
startObserving( imgs );
body {
  font-family: sans-serif;
}
img {
  display: block;
  margin-bottom: 10px;
}
<span></span>
<img class="my-images" src="https://via.placeholder.com/300x100?text=image" width="300">
<span></span>
<img class="my-images" src="https://via.placeholder.com/300x200?text=image" width="300">
<span></span>
<img class="my-images" src="https://via.placeholder.com/300x400?text=image" width="300">

1( 由于某些原因,观察者可能会断开连接。如何重新连接?我尝试使用observer.observe,但它在这里不起作用。

2(第二个问题,手动断开观察器连接的方法是什么?我尝试使用 observer.disconnect((;,但它也不起作用。

走在正确的轨道上,但问题是你试图在它定义的函数之外使用 observer 变量,这意味着在其范围之外,所以它不存在(返回未定义(。

请参阅我的原始代码的更新示例。我已将观察器移动到一个数组中,并使其可以在该函数之外访问,因此您可以正常断开并重新连接它们。

问题只是保留对观察者的引用,就像保留对目标元素的引用一样。

var msg = document.getElementById('msg');
var target = document.querySelectorAll(".someClass");
// an array of observers
var observers = [];
// configuration of the observer
var config = { attributes: true };
for (var i = 0; i < target.length; i++) {
    // create an observer instance
    observers[i] = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            var foo = mutation.target.getAttribute("bgColor")
            if (foo)
                mutation.target.style.backgroundColor = foo;
        });
    });
    // pass in the target node, as well as the observer options
    observers[i].observe(target[i], config);
}
msg.textContent = 'Starting timeouts';
// let's change an attribute in a second
setTimeout(function(){
  target[2].setAttribute('bgColor', 'red');
  msg.textContent = 'Mutation observer should change the box to red';
}, 2000);
setTimeout(function(){
  target[2].setAttribute('bgColor', 'green');
  msg.textContent = 'Mutation observer should change the box to green';
}, 4000);
setTimeout(function(){
  observers[2].disconnect();
  msg.textContent = 'Mutation observer disconnected';
}, 6000);
setTimeout(function(){
  target[2].setAttribute('bgColor', 'blue');
  msg.textContent = 'Mutation observer tries to change the box to blue, but is disconnected';
}, 8000);
setTimeout(function(){
  target[1].setAttribute('bgColor', 'blue');
  msg.textContent = 'Let''s try another box, which is not disconnected, all good';
}, 10000);
setTimeout(function(){
  observers[2].observe(target[2], config);
  msg.textContent = 'Mutation observer reconnected';
}, 12000);
setTimeout(function(){
  target[2].setAttribute('bgColor', 'red');
  msg.textContent = 'Finally, the reconnected mutation observer should change the box to red';
}, 14000);
setTimeout(function(){
  target[1].setAttribute('bgColor', 'white');
  target[2].setAttribute('bgColor', 'white');
  msg.textContent = 'Now try the manual controls below';
  document.getElementById('ctrl').style.display = 'block';
}, 16000);
.someClass {
  width: 50px;
  height: 50px;
  display: inline-block;
  border: 1px solid black
}
#ctrl {display: none}
<div class="someClass"></div>
<div class="someClass"></div>
<div class="someClass"></div>
<div class="someClass"></div>
<p id="msg"></p>
<hr>
<div id="ctrl">
<p>Change attribute: 
<button onclick="target[2].setAttribute('bgColor', 'red');">Red</button>
<button onclick="target[2].setAttribute('bgColor', 'green');">Green</button>
<button onclick="target[2].setAttribute('bgColor', 'blue');">Blue</button>
</p><p>Manage the observer
<button onclick="observers[2].disconnect();">Disconnect</button>
<button onclick="observers[2].observe(target[2], config);">Reconnect</button>
</p>
</div>


更新

根据要求,上述方法将我的第一个示例放在另一个(链接(问题中。基本上,只是一个用于创建观察器的外部化函数。

var msg = document.getElementById('msg');
var target = document.querySelectorAll(".c");
// an array of observers
var observers = [];
// configuration of the observer
var config = { attributes: true };
for (var i = 0; i < target.length; i++) {
  create(target[i], i);
}
function create(t, i) {
  // create an observer instance
  observers[i] = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      var foo = t.getAttribute("bgColor")
      if (foo)
        t.style.backgroundColor = foo;
    });
  });
  // pass in the target node, as well as the observer options
  observers[i].observe(t, config);
}
// let's change an attribute in a second
msg.textContent = 'Starting timeouts';
// let's change an attribute in a second
setTimeout(function(){
  target[2].setAttribute('bgColor', 'red');
  msg.textContent = 'Mutation observer should change the box to red';
}, 2000);
setTimeout(function(){
  target[2].setAttribute('bgColor', 'green');
  msg.textContent = 'Mutation observer should change the box to green';
}, 4000);
setTimeout(function(){
  observers[2].disconnect();
  msg.textContent = 'Mutation observer disconnected';
}, 6000);
setTimeout(function(){
  target[2].setAttribute('bgColor', 'blue');
  msg.textContent = 'Mutation observer tries to change the box to blue, but is disconnected';
}, 8000);
setTimeout(function(){
  target[1].setAttribute('bgColor', 'blue');
  msg.textContent = 'Let''s try another box, which is not disconnected, all good';
}, 10000);
setTimeout(function(){
  observers[2].observe(target[2], config);
  msg.textContent = 'Mutation observer reconnected';
}, 12000);
setTimeout(function(){
  target[2].setAttribute('bgColor', 'red');
  msg.textContent = 'Finally, the reconnected mutation observer should change the box to red';
}, 14000);
setTimeout(function(){
  target[1].setAttribute('bgColor', 'white');
  target[2].setAttribute('bgColor', 'white');
  msg.textContent = 'Now try the manual controls below';
  document.getElementById('ctrl').style.display = 'block';
}, 16000);
.c {
  width: 50px;
  height: 50px;
  display: inline-block;
  border: 1px solid black
}
#ctrl {display: none}
<div class="c"></div>
<div class="c"></div>
<div class="c"></div>
<div class="c"></div>
<p id="msg"></p>
<hr>
<div id="ctrl">
<p>Change attribute: 
<button onclick="target[2].setAttribute('bgColor', 'red');">Red</button>
<button onclick="target[2].setAttribute('bgColor', 'green');">Green</button>
<button onclick="target[2].setAttribute('bgColor', 'blue');">Blue</button>
</p><p>Manage the observer
<button onclick="observers[2].disconnect();">Disconnect</button>
<button onclick="observers[2].observe(target[2], config);">Reconnect</button>
</p>
</div>