重新连接和断开 MutationObserver
Reconnect and disconnect a MutationObserver
这道题是 一道的 sequel 题。不过,前面的没必要看,我只是给感兴趣的读者一个link
有一个观察者,它会对每个元素做出一些反应 class,正如@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
方法即可,但只能在断开连接后进行。
Stops the MutationObserver instance from receiving notifications of DOM mutations. Until the observe()
method is used again, observer's callback will not be invoked.
对已经被观察的元素调用observe()方法不会对观察产生任何影响。至少如果您使用相同的观察者实例进行观察。
NOTE: Adding an observer to an element is just like addEventListener, if you observe the element multiple times it does not make a difference. Meaning if you observe element twice, the observe callback does not fire twice, nor will you have to run disconnect() twice. In other words, once an element is observed, observing it again with the same observer instance will do nothing. However if the callback object is different it will of course add another observer to it.
这里是一个使用一个观察者实例观察几个图像元素的宽度属性的例子。该示例使用超时为每个图像宽度属性设置一个随机值。回调函数将输出更改并断开观察者,然后重新开始整个过程。
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/300x300?text=image" width="300">
1) For some reasons, the observer may be disconnected. How I can reconnect it? I tried to use observer.observe, but it doesn't work here.
2) And the second question, what is the way to manually disconnect an observer? I tried to use observer.disconnect();, but it is also doesn't work.
你走在正确的轨道上,但问题是你试图在它定义的函数之外使用 observer
变量,这意味着在它的范围之外,所以它不存在 ( returns 未定义)。
查看我更新的原始代码示例。我已将观察者移动到一个数组中,并使其可以在该函数之外访问,因此您可以正常断开连接并重新连接它们。
问题只是保留对观察者的引用,就像您保留对目标元素的引用一样。
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>
这道题是
有一个观察者,它会对每个元素做出一些反应 class,正如@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
方法即可,但只能在断开连接后进行。
Stops the MutationObserver instance from receiving notifications of DOM mutations. Until the
observe()
method is used again, observer's callback will not be invoked.
对已经被观察的元素调用observe()方法不会对观察产生任何影响。至少如果您使用相同的观察者实例进行观察。
NOTE: Adding an observer to an element is just like addEventListener, if you observe the element multiple times it does not make a difference. Meaning if you observe element twice, the observe callback does not fire twice, nor will you have to run disconnect() twice. In other words, once an element is observed, observing it again with the same observer instance will do nothing. However if the callback object is different it will of course add another observer to it.
这里是一个使用一个观察者实例观察几个图像元素的宽度属性的例子。该示例使用超时为每个图像宽度属性设置一个随机值。回调函数将输出更改并断开观察者,然后重新开始整个过程。
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/300x300?text=image" width="300">
1) For some reasons, the observer may be disconnected. How I can reconnect it? I tried to use observer.observe, but it doesn't work here.
2) And the second question, what is the way to manually disconnect an observer? I tried to use observer.disconnect();, but it is also doesn't work.
你走在正确的轨道上,但问题是你试图在它定义的函数之外使用 observer
变量,这意味着在它的范围之外,所以它不存在 ( returns 未定义)。
查看我更新的原始代码示例。我已将观察者移动到一个数组中,并使其可以在该函数之外访问,因此您可以正常断开连接并重新连接它们。
问题只是保留对观察者的引用,就像您保留对目标元素的引用一样。
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>