0

I'm trying to make a fake/duplicated scroll element that is synced with the actual x-scroll of a div in native javaScript. The use case for me is on a long table to have the x-scroll be present on screen when you're not at the bottom of the table.

This solves the situation of having a really wide table with a min-width that exceeds the current page/view-port width, causing the table to side/x-scroll. However, if the table is very long, the scroll can only be set on top or bottom of the table. That means if people are mid-way down the table and want to scroll across to see all of the columns, they would have difficulty in doing it there.

See image:

enter image description here

Yep, it's been done to death IN JQUERY. According to my research, no one on SO knows or has been interested in doing this in native javaScript (esp 2020). My version for reference is also in jQuery, it needs to be converted.

  $dupScroll.scroll(function () {
    if (scrolling) {
      scrolling = false;
      return true;
    }
    scrolling = true;
    $tableParent.scrollLeft($dupScroll.scrollLeft());
  });
  $tableParent.scroll(function () {
    if (scrolling) {
      scrolling = false;
      return true;
    }
    scrolling = true;
    $dupScroll.scrollLeft($tableParent.scrollLeft());
  });

All the jQuery solutions:

  1. How to Scroll two div's at the same time?
  2. synchronizing scrolling between 2 divs
  3. synchronizing scrolling between 2 divs with different text size
  4. How to sync the scroll of two divs by ID
  5. synchronise scrolling of two divs

Help is appreciated and will be useful for all the people needing to do the same post-jQuery. I'm currently working on this myself but running into snags here and there, the 1st being attaching a scroll event onto an element. If I make something that works, I'll post it here. Thanks.

bot19
  • 664
  • 8
  • 18
  • 1
    In CSS, something like `#container{ overflow:scroll; }` shows the scrollbars. Your question is actually very unclear. Synchronized as in percentage being the same? Do the math. – StackSlave Dec 02 '20 at 23:47
  • 2
    `div1.addEventListener( 'scroll', e => div2.scrollTop = div1.scrollTop)` should do the trick. Keep in mind that JS does not trigger a scroll event on the second element as its progrmatically set, so there's no need for a flag to see which is being scrolled. – somethinghere Dec 02 '20 at 23:55
  • thanks @somethinghere that definitely solves 1 huddle, I'll work on solving the side-scroll issue as I'm syncing x-scroll not y-scroll. Thanks again! – bot19 Dec 03 '20 at 00:20
  • 1
    @bot19 Check my answer below, you can just replace any instance of `scrollTop` with `scrollLeft` (and `scrollHeight` with `scrollWidth`, `clientHeight` with `clientWidth` for the second bit of my answer) – somethinghere Dec 03 '20 at 00:22
  • 1
    @bot19 Actually, I have edited my answer to include both directions being synchronised. Hope that helps! – somethinghere Dec 03 '20 at 00:26
  • @somethinghere thanks! If you include a scrollLeft demo I'll accept it, just want it straight forward for anyone searching this in the future as that was the original question - thanks again! (...beat me to it!) – bot19 Dec 03 '20 at 00:27
  • 1
    @bot19 Do you still need that or is both directions also okay? I find it simple enough to understand either way, with height and top being pretty clear as to how it works. – somethinghere Dec 03 '20 at 00:28
  • So, like I stated... you needed to "Do the math.". – StackSlave Dec 03 '20 at 00:29

1 Answers1

4

Here's the simple way to keep two divs aligned. Javascript doesn't dispatch event on actions from scripts by default, so there's no need to keep track of which div is being scrolled.

const divs = document.querySelectorAll( 'div' );

divs.forEach(div => div.addEventListener( 'scroll', e => {
  
  divs.forEach(d => {
  
    d.scrollTop = div.scrollTop;
    d.scrollLeft = div.scrollLeft;
  
  });
  
}) );
html, body {

  height: 100%;
  
}
body {

  display: flex;
  padding: 0;
  margin: 0;

}
div {

  width: 50%;
  height: 100%;
  overflow: scroll;

}
span {

  width: 200vw;
  height: 300vh;
  display: block;
  background: linear-gradient(90deg, transparent, yellow), linear-gradient( 0deg, red, blue, green );
  
}
#div2 {

  margin-top: 30px;
  
}
<div id="div1"><span></span></div>
<div id="div2"><span></span></div>

With Relative Scroll in Different Sized Containers

If you want to accomplish this with differently sized containers and relative scroll, just normalize the scroll value and multiply it again:

const divs = document.querySelectorAll( 'div' );

divs.forEach(div => div.addEventListener( 'scroll', e => {
  
  const offsetTop = div.scrollTop / (div.scrollHeight - div.clientHeight);
  
  const offsetLeft = div.scrollLeft / (div.scrollWidth - div.clientWidth);
  
  divs.forEach(d => {
  
    d.scrollTop = offsetTop * (d.scrollHeight - d.clientHeight);
    d.scrollLeft = offsetLeft * (d.scrollWidth - d.clientWidth);
    
  });
  
}) );
html, body {

  height: 100%;
  
}
body {

  display: flex;
  padding: 0;
  margin: 0;

}
div {

  width: 50%;
  height: 100%;
  overflow: scroll;

}
span {

  width: 200vw;
  height: 300vh;
  display: block;
  background: linear-gradient(90deg, transparent, yellow), linear-gradient( 0deg, red, blue, green );
  
}
#div2 span {
  
  height: 500vh;
  width: 500vw;
  
}
<div id="div1"><span></span></div>
<div id="div2"><span></span></div>
somethinghere
  • 16,311
  • 2
  • 28
  • 42
  • 1
    is it just me (or my linux chrome) that for the relative scroll, if you scrub it a little (at least for x-scroll) it moves on its own afterwards? – bot19 Dec 03 '20 at 00:29
  • 1
    @bot19 Not seeing that on my device, I'm on safari on mac and it seems pretty consistent, so can't really say. I also have a feeling that 'fixing' that is gonna turn this answer really ugly with loads of checks - even if its just a glitch in your rendering engine :) I'd rather keep it simple and to the point :D – somethinghere Dec 03 '20 at 00:30