javascriptmobile-webkit

Mutation Observer not working but Polling works


I have the following code injected into a page on document start. It's supposed to listen for video elements and changes to the video element's src attribute.

var observer;

function logStuff(node) {
    console.log(node.src);
}

function checkAllVideos() {
    document.querySelectorAll('video').forEach(function (node) {
        logStuff(node);

       node.onloadedmetadata = function() {
        logStuff(node);
       }
    });
}

function addObserver() {
    function checkNode(node) {
        if (node.constructor.name == "HTMLVideoElement") {
            logStuff(node);

            node.onloadedmetadata = function() {
                logStuff(node);
            }
        }
    }

    observer = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
            mutation.addedNodes.forEach(function (node) {
                checkNode(node);
            });
        });
    });
    observer.observe(document, {subtree: true, childList: true, characterData: true});
}


addObserver();
checkAllVideos();

The polling version of the above is:

window.setInterval(function(){
    checkAllVideos();
}, 1000);

For some weird reason, when injected into https://dailymotion.com, the mutation observer never works but the polling code works..

If I inject the mutation observer code into youtube's web-page, it works fine.. Both versions of the code works on youtube but only the polling setInterval code works on dailymotion. Any ideas why?

I just want to be notified when any HTMLVideoElement (<video>) changes its src attribute.

Why does the mutation observer not work?

The web-page adds the video tag via: <link rel="alternate" href="https://www.dailymotion.com/services/oembed?url=https%3A%2F%2Fwww.dailymotion.com%2Fvideo%2Fx7ojouk" type="application/json+oembed" title="Surgeon Explains How to Tie Surgical Knots" data-rh="true">

and that href contains an iFrame.


Solution

  • Instead of observing entire document. I have created an observer for each video element and it seems to work.

    var observer;
    var btn = document.getElementById("setsrc");
    btn.addEventListener("click", () => {
      /* let v = document.getElementsByTagName("video");
        v[0].children[0].remove();
        v[1].height = "200"; */
      let vi = document.createElement("video");
      document.body.appendChild(vi);
      vi.height = "200";
    });
    
    const config = {
      attributes: true,
      childList: true,
      subtree: true
    };
    
    function checkAllVideos() {
      document.querySelectorAll('video').forEach(observeNode);
      observeNode(document.body, config);
    }
    
    function observeNode(node) {
      const callback = function(mutationsList, observer) {
        for (let mutation of mutationsList) {
          if (mutation.type === 'childList') {
            let addedNodes = mutation.addedNodes;
            if (addedNodes && addedNodes.length) {
              addedNodes.forEach(n => {
                if (n.nodeName === "VIDEO") observeNode(n, config);
              });
            }
            console.log('A child node has been added or removed.');
          } else if (mutation.type === 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
          }
        }
      };
    
      // Create an observer instance linked to the callback function
      const observer = new MutationObserver(callback);
    
      // Start observing the target node for configured mutations
      observer.observe(node, config);
    }
    checkAllVideos();
    <div>
      <video width="400" controls id="vid">
      <source src="mov_bbb.mp4" type="video/mp4">
      <source src="mov_bbb.ogg" type="video/ogg">
      Your browser does not support HTML5 video.
      </video>
      
      <video width="400" controls id="vid2">
      <source src="mov_bbb.mp4" type="video/mp4">
      <source src="mov_bbb.ogg" type="video/ogg">
      Your browser does not support HTML5 video.
      </video>
    </div>
    <div>
      <button id="setsrc">Set</button>
    </div>