2

I have two boxes, one is smaller then the other. The small box contains the same text as the other, but since it's smaller auto scrolling takes a longer time. I would like the big box to end scrolling the same time the small box ends.

let stepSize = 0.5;
let masterSpeed = 50;
let masterElement = document.getElementById("master-box");
let slaveElement = document.getElementById("slave-box");

var scroll = 0;

window.setInterval(function(){ 
  if(masterElement.scrollTop > scroll)
  scroll = masterElement.scrollTop;
  masterElement.scrollTo({ top: scroll, behavior: 'smooth' })
  scroll += 1; 
}, masterSpeed); //THIS WILL RUN IN EVERY 50 MILISECONDS

// Match slave speed to master speed
let masterDistance = masterElement.scrollHeight - masterElement.clientHeight;
let masterScrollTime = masterDistance / masterSpeed;
let syncedSpeed = masterDistance / masterScrollTime;

window.setInterval(function(){ 
  if(slaveElement.scrollTop > scroll)
  scroll = slaveElement.scrollTop;
  slaveElement.scrollTo({ top: scroll, behavior: 'smooth' })
  scroll += 1; 
}, syncedSpeed); //THIS WILL RUN IN EVERY 50 MILISECONDS
.container {
  display: flex;
}
span {
  display: block;
  height : 3px;
  background: red;
}
.box1{
  width: 100px;
  height: 200px;
  overflow-y: auto;
  border: 2px solid blue;
  margin: 10px;
}
.box2{
  width: 200px;
  height: 300px;
  overflow-y: auto;
  border: 2px solid blue;
  margin: 10px;
}
<div class="container">
<div class="box1" id="master-box">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!<span></span></div>
<div class="box2" id="slave-box">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!<span></span></div>
</div>
I wrestled a bear once.
  • 22,983
  • 19
  • 69
  • 116
Zoli
  • 65
  • 6
  • Do not use `setInterval` twice. Instead, update the second one using a percentage of scroll instead of just using the same `scrollTop` – Ruan Mendes Dec 03 '21 at 14:22

2 Answers2

1

Use percentages rather than the scroll index. Make a couple of helper functions and it'll be much easier to understand. Behold:

let masterElement = document.getElementById("master-box");
let slaveElement = document.getElementById("slave-box");

// Scroll to a percentage of the vertical height
function scrollToPct(element, pct){
  var scroll = (element.scrollHeight - element.clientHeight) / 100 * pct;
  element.scrollTo({ top: scroll, behavior: 'smooth' });
}

// Get the percentage of vertical scroll
function getScrollPct(element){
  return element.scrollTop / (element.scrollHeight - element.clientHeight) * 100;
}

// Listen to the mastre element and update the slave element accordingly
masterElement.addEventListener('scroll', function(){
  var scroll_pct = getScrollPct(this);
  scrollToPct(slaveElement, scroll_pct);
});

// turn on the autoscroll
setInterval(function(){
  var scroll_pct = getScrollPct(masterElement);
  scrollToPct(masterElement, scroll_pct + 5);
}, 10);
.container {
  display: flex;
}
span {
  display: block;
  height : 3px;
  background: red;
}
.box1{
  width: 100px;
  height: 200px;
  overflow-y: auto;
  border: 2px solid blue;
  margin: 10px;
}
.box2{
  width: 200px;
  height: 300px;
  overflow-y: auto;
  border: 2px solid blue;
  margin: 10px;
}
<div class="container">
<div class="box1" id="master-box">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!<span></span></div>
<div class="box2" id="slave-box">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!<span></span></div>
</div>
I wrestled a bear once.
  • 22,983
  • 19
  • 69
  • 116
  • Doesn't seem to be working for me?? Not even animating the scroll... – Ruan Mendes Dec 03 '21 at 14:54
  • @JuanMendes - I see.. Chrome has a problem with it. I'll check it out. – I wrestled a bear once. Dec 03 '21 at 14:56
  • Thank you very much, the problem here is that I need to implement this in a way that they are not on the same page, the master sends a play signal with his height and speed and I need to convert those metrics to milliseconds that will mach up with master. – Zoli Dec 03 '21 at 15:04
  • @Zoli - How is that a problem? Send the signal to update the slave inside the event handler. – I wrestled a bear once. Dec 03 '21 at 15:10
  • @Iwrestledabearonce. I'm trying this via firebase real time database, I'm saving the rounded percentage together with the height of the master. I need to use these metrics to achieve smooth scrolling but I cant use the rounded percentage (I will use this to check if its very off and if it is I reset it to the correct percentage - this is only if user has a bad connection) I need some sort of formula that can give me the correct milliseconds to pass to the `setInterval`. – Zoli Dec 03 '21 at 15:21
  • @Zoli Then it seems you are asking the wrong question??? You asked to sync the scrolling between two scrollable elements on a page. – Ruan Mendes Dec 03 '21 at 15:23
  • If you're trying to sync state between two remote devices you need to use websockets. They will give you a direct connection between two browsers. [Here's a library](https://github.com/Pamblam/browser-mirror) I wrote with node that may or may not be useful to you. – I wrestled a bear once. Dec 03 '21 at 15:28
1

Do not use setInterval twice. Instead, update the second one using a percentage of scroll instead of just using the same scrollTop.

Also, setting the behavior to smooth was causing animations to run which could interfere with your scrolling commands.

Lastly, you need to take the scrollHeight and clientHeight into account.

const masterSpeed = 50;
const stepSize = 10;
const masterElement = document.getElementById("master-box");
const slaveElement = document.getElementById("slave-box");

const masterScrollHeight = masterElement.scrollHeight;
const slaveScrollHeight = slaveElement.scrollHeight;
const masterClientHeight = masterElement.clientHeight;
const slaveClientHeight = slaveElement.clientHeight;

const masterHeight = masterScrollHeight - masterClientHeight;
const slaveHeight = slaveScrollHeight - slaveClientHeight;
/*
console.log({
  masterScrollHeight,
  slaveScrollHeight,
  masterClientHeight,
  slaveClientHeight,
  masterHeight,
  slaveHeight
});
*/
let interval = window.setInterval(function() {
  const currentMasterScrollTop = masterElement.scrollTop;
  const newMasterScrollTop = currentMasterScrollTop + stepSize;
  if (newMasterScrollTop > masterHeight) {

    window.clearInterval(interval);
    interval = null;
  }

  const percentageMasterDone = newMasterScrollTop / masterHeight;
  const newSlaveScrollTop = Math.ceil(percentageMasterDone * slaveHeight);
  /*
    console.log({
      currentMasterScrollTop,
      newMasterScrollTop,
      percentageMasterDone,
      newMasterScrollTop,
      newSlaveScrollTop
    });
    */
  masterElement.scrollTo({
    top: newMasterScrollTop
  });
  slaveElement.scrollTo({
    top: newSlaveScrollTop
  })

}, masterSpeed); //THIS WILL RUN IN EVERY 50 MILISECONDS
.container {
  display: flex;
}

span {
  display: block;
  height: 3px;
  background: red;
}

.box1 {
  width: 100px;
  height: 200px;
  overflow-y: auto;
  border: 2px solid blue;
  margin: 10px;
}

.box2 {
  width: 200px;
  height: 300px;
  overflow-y: auto;
  border: 2px solid blue;
  margin: 10px;
}
<div class="container">
  <div class="box1" id="master-box">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur,
    adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur
    sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam
    autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!<span></span></div>
  <div class="box2" id="slave-box">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur,
    adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur
    sequi harum sunt adipisci aperiam autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!Lorem ipsum dolor sit amet consectetur, adipisicing elit. Qui, aspernatur sequi harum sunt adipisci aperiam
    autem perspiciatis quidem delectus corporis nulla modi sit id, odit minus. Natus tenetur iusto temporibus!<span></span></div>
</div>
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • This is perfect thank you very much. I was looking for a solution for days. – Zoli Dec 03 '21 at 22:24
  • If you could also explain the math I would be eternally grateful :) – Zoli Dec 03 '21 at 22:38
  • @Zoli Which part? I tried to document it heavily with variable names It did take a lot of playing around. The key was understanding that `scrollTop` did not have to be set to `scrollHeight` to scroll the element all the way, it only needs to be set to `scrollHeight - clientHeight` and you use that as the 100% of your `scrollTop` – Ruan Mendes Dec 03 '21 at 22:55