重新连接并断开MutationObserver

时间:2016-02-09 11:23:29

标签: javascript mutation-observers

这个问题是this的续集。但是,没有必要阅读之前的内容,我只是为感兴趣的读者提供链接。

有一个观察者会像@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();,但它也不起作用。

2 个答案:

答案 0 :(得分:8)

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

  

disconnect()

     

停止MutationObserver实例接收DOM突变的通知。在再次使用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 );方法之前,不会调用观察者的回调。

对已经被观察的元素调用observe()方法不会对观察产生任何影响。至少如果您使用相同的观察者实例进行观察。

  

注意:向元素添加观察者就像addEventListener一样,如果你多次观察元素它没有什么区别。这意味着如果您观察元素两次,则观察回调不会触发两次,也不必两次运行disconnect()。换句话说,一旦观察到一个元素,用同一个观察者实例再次观察它就什么也不做。但是,如果回调对象不同,它当然会向它添加另一个观察者。

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

&#13;
&#13;
body {
  font-family: sans-serif;
}

img {
  display: block;
  margin-bottom: 10px;
}
&#13;
<span></span>
<img class="my-images" src="http://placehold.it/300x100?text=image" width="300">
<span></span>
<img class="my-images" src="http://placehold.it/300x200?text=image" width="300">
<span></span>
<img class="my-images" src="http://placehold.it/300x300?text=image" width="300">
&#13;
echo '<table border=1>';
$data = range(1, 30);
for($count = 0; $count < count($data); $count++){
  echo "<tr>\n";
  if($count < 4) {
    echo "\t<td>$data[$count]</td>\n";
    $count++;
    echo "\t<td>$data[$count]</td>\n";
  } else {
    echo "\t<td colspan=2>$data[$count]</td>\n";
  }
  echo "</tr>\n";
}
echo '</table>';
&#13;
&#13;
&#13;

答案 1 :(得分:2)

  

1)由于某些原因,观察者可能会断开连接。我怎么重新连接呢?我试着使用observer.observe,但它在这里没有用。

     

2)第二个问题,手动断开观察者的方式是什么?我尝试使用observer.disconnect();,但它也没有用。

你走在正确的轨道上,但问题是你试图在它定义的函数之外使用observer变量,这意味着它的范围之外,所以它没有& #39; t存在(返回undefined)。

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

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

&#13;
&#13;
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);
&#13;
.someClass {
  width: 50px;
  height: 50px;
  display: inline-block;
  border: 1px solid black
}

#ctrl {display: none}
&#13;
<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>
&#13;
&#13;
&#13;

更新

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

&#13;
&#13;
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);
&#13;
.c {
  width: 50px;
  height: 50px;
  display: inline-block;
  border: 1px solid black
}
#ctrl {display: none}
&#13;
<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>
&#13;
&#13;
&#13;