skip to Main Content

I’m writing a tampermonkey script to add speed buttons next to Facebook videos. The script get all the video elements in the page, get their position, and bases on that position to place the button next to them. The script runs fine at the beginning. But as I scroll and the page loads more videos, the button position doesn’t match their video anymore. As a result, videos won’t have speed buttons next to them. I suspect that by loading more videos, the code messed up somewhere and calculate the video position from the top of the page wrong, so that the button is placed wrong consequently. Can someone suggest a way to fix this? My code is as below:

function addButton(){

function getOffset( el ) {//get the position of the element respective to the top and the left of the page

    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
        _x += el.offsetLeft - el.scrollLeft;
        _y += el.offsetTop - el.scrollTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}

    function setButton(video){//add buttons next to the videos
      var elDistanceToTop = window.pageYOffset + video.getBoundingClientRect().top
      var y=elDistanceToTop;//get the distance from the element to the TOP of the page
        //var y = getOffset( video ).top;
        var x=getOffset( video ).left;//get the distance from the element to the LEFT of the page
      var button1 = document.createElement('button');
      button1.textContent = '1x';
      // Set button position
      button1.style.position = 'absolute';
      button1.style.left = x+video.getBoundingClientRect().width+ 'px';//place button to the left of video
      button1.style.top = y+ 'px';//position from the top of the page to place the button
        button1.style.width='58px';
        button1.style.height=video.getBoundingClientRect().height/3+'px';
        button1.addEventListener('click', function () {
      // Increase speed by 1, maximum speed is 4.0
                video.playbackRate = 1;
    });
      // Add button to the document body
      document.body.appendChild(button1);

        var button2 = document.createElement('button');
      button2.textContent = '1.5x';
      // Set button position
      button2.style.position = 'absolute';
      button2.style.left = x+video.getBoundingClientRect().width+ 'px';
      button2.style.top = y+video.getBoundingClientRect().height/3+ 'px';
        button2.style.width='58px';
        button2.style.height=video.getBoundingClientRect().height/3+'px';
        button2.addEventListener('click', function () {
      // Increase speed by 1.5, maximum speed is 4.0
                video.playbackRate = 1.5;
    });
      // Add button to the document body
      document.body.appendChild(button2);

         var button3 = document.createElement('button');
      button3.textContent = '2x';
      // Set button position
      button3.style.position = 'absolute';
      button3.style.left = x+video.getBoundingClientRect().width+ 'px';
      button3.style.top = y+video.getBoundingClientRect().height*2/3+ 'px';
        button3.style.width='58px';
        button3.style.height=video.getBoundingClientRect().height/3+'px';
        button3.addEventListener('click', function () {
      // Increase speed by 2, maximum speed is 4.0
                video.playbackRate = 2;
    });
      // Add button to the document body
      document.body.appendChild(button3);
    }
   const videoElement = document.querySelectorAll('video');

       videoElement.forEach((video, index) => {
    //check to see if the video had had buttons
    const loadedBefore = video.getAttribute('data-loaded') === 'true';

    if (loadedBefore) {//if it has, do nothing

    } else {//if it doesn't have buttons, add them
       setButton(video);
    }
});

       videoElement.forEach((video, index) => {//mark all exsisting video on the page as button-having,
           //so as not to add button to them again
    video.setAttribute('data-loaded', 'true');
    
});

   }

setInterval(addButton, 3000);//the loop to check and add button again and again

I attached 2 image of how the button looks like at the beginning and how they look after a few scrolls on the page
before
After

2

Answers


  1. modify your existing code to include the MutationObserver:

    function addButton() {
        // Your existing code...
    }
    
    // Create a new observer
    const observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList') {
          // If new nodes are added, run the addButton function
          addButton();
        }
      });
    });
    
    // Start observing the document with the configured parameters
    observer.observe(document, { childList: true, subtree: true });
    
    // Your existing setInterval call...
    setInterval(addButton, 3000);

    In this modified version of your code, a MutationObserver is created that watches for changes in the DOM. If new nodes are added to the DOM, the addButton function is called. This ensures that your speed buttons are added to new videos as they are loaded onto the page.

    Login or Signup to reply.
  2. In this case, I suspect the problem to be where you are adding your buttons.

    Instead of adding them a certain distance from the top of the page, try to identify a parent container that bounds that group of elements and add it in there. Then, as the that parent containers change places on the screen, your buttons will change with them.

    Otherwise, you will need to update the screen locations for all buttons in a loop. More work and a little more difficult. However, if you choose such a route then OOKWUDILI’s reference to the mutation observer would be helpful.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search