Search code examples
javascriptmutation-observers

Reconnect and disconnect a MutationObserver


This question is the sequel of this one. However, it is not necessary to read the previous, I'm just giving a link for interested readers.

There is an observer, which will react on every element with some class, as @Shomz suggested:

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);
}

So, there are two closely intertwined questions.

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.


Solution

  • 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.

    You are on the right track, but the thing is you're trying to use the observer variable outside of the function it's defined in, meaning outside of its scope, so it doesn't exist (returns undefined).

    See my updated example of your original code. I've moved the observers into an array and made it accessible outside of that function, so you can disconnect and reconnect them normally.

    The issue was just keeping references to observers, just like you were keeping references to the target elements.

    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>


    UPDATE

    As requested, the above approach put inside my first example in the other (linked) question. Basically, just an externalized function for creating observers.

    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>