Synchronized scrolling using jQuery?

18,253

Solution 1

You can use element.scrollTop / (element.scrollHeight - element.offsetHeight) to get the percentage (it'll be a value between 0 and 1). So you can multiply the other element's (.scrollHeight - .offsetHeight) by this value for proportional scrolling.

To avoid triggering the listeners in a loop you could temporarily unbind the listener, set the scrollTop and rebind again.

var $divs = $('#div1, #div2');
var sync = function(e){
    var $other = $divs.not(this).off('scroll'), other = $other.get(0);
    var percentage = this.scrollTop / (this.scrollHeight - this.offsetHeight);
    other.scrollTop = percentage * (other.scrollHeight - other.offsetHeight);
    // Firefox workaround. Rebinding without delay isn't enough.
    setTimeout( function(){ $other.on('scroll', sync ); },10);
}
$divs.on( 'scroll', sync);

http://jsfiddle.net/b75KZ/5/

Solution 2

Runs like clockwork (see DEMO)

$(document).ready(function(){

  var master = "div1"; // this is id div
  var slave = "div2"; // this is other id div
  var master_tmp;
  var slave_tmp;
  var timer;

  var sync = function ()
  {
    if($(this).attr('id') == slave)
    {
      master_tmp = master;
      slave_tmp = slave;
      master = slave;
      slave = master_tmp;
    }

    $("#" + slave).unbind("scroll");

    var percentage = this.scrollTop / (this.scrollHeight - this.offsetHeight);

    var x = percentage * ($("#" + slave).get(0).scrollHeight - $("#" + slave).get(0).offsetHeight);

    $("#" + slave).scrollTop(x);

    if(typeof(timer) !== 'undefind')
      clearTimeout(timer);

    timer = setTimeout(function(){ $("#" + slave).scroll(sync) }, 200)
  }

  $('#' + master + ', #' + slave).scroll(sync);

});

Solution 3

This is what I'm using. Just call the syncScroll(...) function with the two elements you want to synchronize. I found pawel's solution had issues with continuing to slowly scroll after the mouse or trackpad was actually done with the operation.

See working example here.

// Sync up our elements.
syncScroll($('.scroll-elem-1'), $('.scroll-elem-2'));


/***
*   Synchronize Scroll
*   Synchronizes the vertical scrolling of two elements.
*   The elements can have different content heights.
*
*   @param $el1 {Object}
*       Native DOM element or jQuery selector.
*       First element to sync.
*   @param $el2 {Object}
*       Native DOM element or jQuery selector.
*       Second element to sync.
*/
function syncScroll(el1, el2) {
  var $el1 = $(el1);
  var $el2 = $(el2);

  // Lets us know when a scroll is organic
  // or forced from the synced element.
  var forcedScroll = false;

  // Catch our elements' scroll events and
  // syncronize the related element.
  $el1.scroll(function() { performScroll($el1, $el2); });
  $el2.scroll(function() { performScroll($el2, $el1); });

  // Perform the scroll of the synced element
  // based on the scrolled element.
  function performScroll($scrolled, $toScroll) {
    if (forcedScroll) return (forcedScroll = false);
    var percent = ($scrolled.scrollTop() / 
      ($scrolled[0].scrollHeight - $scrolled.outerHeight())) * 100;
    setScrollTopFromPercent($toScroll, percent);
  }

  // Scroll to a position in the given
  // element based on a percent.
  function setScrollTopFromPercent($el, percent) {
    var scrollTopPos = (percent / 100) *
      ($el[0].scrollHeight - $el.outerHeight());
    forcedScroll = true;
    $el.scrollTop(scrollTopPos);
  }
}

Solution 4

If the divs are of equal sizes then this code below is a simple way to scroll them synchronously:

scroll_all_blocks: function(e) {
        var scrollLeft = $(e.target)[0].scrollLeft;

        var len = $('.scroll_class').length;
        for (var i = 0; i < len; i++)
        {
            $('.scroll_class')[i].scrollLeft = scrollLeft;
        }

    }

Here im using horizontal scroll, but you can use scrollTop here instead. This function is call on scroll event on the div, so the e will have access to the event object. Secondly, you can simply have the ratio of corresponding sizes of the divs calculated to apply in this line $('.scroll_class')[i].scrollLeft = scrollLeft;

Solution 5

I solved the sync scrolling loop problem by setting the scroll percentage to fixed-point notation: percent.toFixed(0), with 0 as the parameter. This prevents mismatched fractional scrolling heights between the two synced elements, which are constantly trying to "catch up" with each other. This code will let them catch up after at most a single extra step (i.e., the second element may continue to scroll an extra pixel after the user stops scrolling). Not a perfect solution or the most sophisticated, but certainly the simplest I could find.

var left = document.getElementById('left');
var right = document.getElementById('right');
var el2;
var percentage = function(el) { return (el.scrollTop / (el.scrollHeight - el.offsetHeight)) };

function syncScroll(el1) {
  el1.getAttribute('id') === 'left' ? el2 = right : el2 = left;
  el2.scrollTo( 0, (percentage(el1) * (el2.scrollHeight - el2.offsetHeight)).toFixed(0) ); // toFixed(0) prevents scrolling feedback loop
}

document.getElementById('left').addEventListener('scroll',function() {
  syncScroll(this);
});
document.getElementById('right').addEventListener('scroll',function() {
  syncScroll(this);
});

Share:
18,253

Related videos on Youtube

Nauphal
Author by

Nauphal

Full stack web developer Upwork | Elance | Linkedin

Updated on June 28, 2022

Comments

  • Nauphal
    Nauphal almost 2 years

    I am trying to implement synchronized scrolling for two DIV with the following code.

    DEMO

    $(document).ready(function() {
        $("#div1").scroll(function () { 
            $("#div2").scrollTop($("#div1").scrollTop());
        });
        $("#div2").scroll(function () { 
            $("#div1").scrollTop($("#div2").scrollTop());
        });
    });
    

    #div1 and #div2 is having the very same content but different sizes, say

    #div1 {
     height : 800px;
     width: 600px;
    }
    #div1 {
     height : 400px;
     width: 200px;
    }
    

    With this code, I am facing two issues.

    1) Scrolling is not well synchronized, since the divs are of different sizes. I know, this is because, I am directly setting the scrollTop value. I need to find the percentage of scrolled content and calculate corresponding scrollTop value for the other div. I am not sure, how to find the actual height and current scroll position.

    2) This issue is only found in firefox. In firefox, scrolling is not smooth as in other browsers. I think this because the above code is creating a infinite loop of scroll events. I am not sure, why this is only happening with firefox. Is there any way to find the source of scroll event, so that I can resolve this issue.

    Any help would be greatly appreciated.

Related