Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Synchronized scrolling using jQuery?

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.

like image 224
Nauphal Avatar asked Sep 23 '13 05:09

Nauphal


4 Answers

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/

like image 169
pawel Avatar answered Nov 19 '22 12:11

pawel


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);

});
like image 39
ktretyak Avatar answered Nov 19 '22 13:11

ktretyak


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);
  }
}
like image 3
goozbox Avatar answered Nov 19 '22 12:11

goozbox


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;

like image 2
Rahul Dole Avatar answered Nov 19 '22 12:11

Rahul Dole