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
2
Answers
modify your existing code to include the MutationObserver:
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.
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.