5

I've successfully got two divs on a page, each with a scrollbar, and using jQuery, when one div is scrolled, the other scrolls accordingly.

$('#left').scroll(function () {
    $('#right').scrollTop($(this).scrollTop());
});

$('#right').scroll(function () {
    $('#left').scrollTop($(this).scrollTop());
});

This works fine as along as you use the keyboard or click on the scroll bar and drag to do the scrolling, but if you use the mouse scrollwheel it's incredibly slow, due, I think, to the fact that there's a 'feedback loop'.

Is there a way I can prevent this loop so that scrolling with the mouse scrollwheel works smoothly?

Here's a fiddle showing the problem.

Philip Stratford
  • 4,513
  • 4
  • 45
  • 71
  • The accepted answer didn't really work for me (probably because of the sheer size of the tables I'm trying to synchronize-scroll) but [this answer on a different question](https://stackoverflow.com/questions/39261456/reducing-scroll-jank-when-using-overflow-y-scroll/39377543#39377543) helped a lot! – Constantinos Dec 08 '17 at 13:30

1 Answers1

6

The problem is, that you have a scroll event listener on both divs. The event is bouncing then. One workaroung would be to unbind the other listener with off and reassign it afterwards.

But there should be a short delay, because the browser takes some time for setting the scrollTop to the new value. One way would be using setTimeout for this task.

// a helper variable
var timeout;

$('#left, #right').on("scroll", function callback() {
    // clear the 'timeout' every 'scroll' event call
    // to prevent re-assign 'scroll' event to other element
    // before finished scrolling
    clearTimeout(timeout);

    // get the used elements
    var source = $(this),
        target = $(source.is("#left") ? '#right' : '#left');

    // remove the callback from the other 'div' and set the 'scrollTop'
    target.off("scroll").scrollTop(source.scrollTop());

    // create a new 'timeout' and reassign 'scroll' event
    // to other 'div' on 100ms after the last event call
    timeout = setTimeout(function() {
        target.on("scroll", callback);
    }, 100);
});

Working example:

// a helper variable
var timeout;

$('#left, #right').on("scroll", function callback() {
    // clear the 'timeout' every 'scroll' event call
    // to prevent re-assign 'scroll' event to other element
    // before finished scrolling
    clearTimeout(timeout);

    // get the used elements
    var source = $(this),
        target = $(source.is("#left") ? '#right' : '#left');

    // remove the callback from the other 'div' and set the 'scrollTop'
    target.off("scroll").scrollTop(source.scrollTop());

    // create a new 'timeout' and reassign 'scroll' event
    // to other 'div' on 100ms after the last event call
    timeout = setTimeout(function() {
        target.on("scroll", callback);
    }, 100);
});
div {
  width: 50%;
  height: 200px;
  overflow-y: scroll;
}

#left {
  float: left;
}

#right {
  float: right;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="left">
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>10th P Tag</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>20th P Tag</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>30th P Tag</p>
</div>
<div id="right">
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>10th P Tag</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>20th P Tag</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>Content Here</p>
  <p>30th P Tag</p>
</div>

jsfiddle

eisbehr
  • 12,243
  • 7
  • 38
  • 63
  • 1
    Well, I'm not sure if this is faster. `id` selectors are pretty fast and `not()` is a filter action. Maybe it is faster, maybe not. But your approach is definitly working! @Guedes I've did it here for a working example: http://jsfiddle.net/sgcer/1794/ – eisbehr Aug 03 '16 at 11:43
  • I just implemented this in my project and it works perfectly for me! Thanks! – Philip Stratford Aug 03 '16 at 12:35