Синхронизированная прокрутка с помощью jQuery?
Я пытаюсь реализовать синхронизированную прокрутку для двух DIV
со следующим кодом.
DEMO
$(document).ready(function() {
$("#div1").scroll(function () {
$("#div2").scrollTop($("#div1").scrollTop());
});
$("#div2").scroll(function () {
$("#div1").scrollTop($("#div2").scrollTop());
});
});
#div1
и #div2
имеет тот же контент, но разные размеры, скажем
#div1 {
height : 800px;
width: 600px;
}
#div1 {
height : 400px;
width: 200px;
}
С помощью этого кода я столкнулся с двумя проблемами.
1) Прокрутка не очень хорошо синхронизирована, так как divs имеют разные размеры. Я знаю, это потому, что я непосредственно устанавливаю значение scrollTop
. Мне нужно найти процент прокрученного содержимого и рассчитать соответствующее значение scrollTop
для другого DIV
. Я не уверен, как найти фактическую высоту и текущую позицию прокрутки.
2) Эта проблема встречается только в firefox
. В firefox прокрутка не является гладкой, как в других браузерах. Я думаю, это потому, что приведенный выше код создает бесконечный цикл событий прокрутки.
Я не уверен, почему это происходит только с firefox. Есть ли способ найти источник события прокрутки, чтобы я мог решить эту проблему.
Любая помощь будет принята с благодарностью.
Ответы
Ответ 1
Вы можете использовать element.scrollTop / (element.scrollHeight - element.offsetHeight)
для получения процента (это будет значение между 0
и 1
). Таким образом, вы можете умножить другой элемент (.scrollHeight - .offsetHeight)
на это значение для пропорциональной прокрутки.
Чтобы не запускать прослушиватели в цикле, вы можете временно отвязать прослушиватель, установите scrollTop
и снова заново.
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/
Ответ 2
Если divs имеют одинаковые размеры, то этот код ниже - простой способ их синхронного прокрутки:
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;
}
}
Здесь im использует горизонтальную прокрутку, но вместо этого вы можете использовать scrollTop. Эта функция вызывает событие scroll
в div, поэтому e
будет иметь доступ к объекту события.
Во-вторых, вы можете просто иметь отношение соответствующих размеров divs, рассчитанных для применения в этой строке $('.scroll_class')[i].scrollLeft = scrollLeft;
Ответ 3
Это то, что я использую. Просто вызовите функцию syncScroll(...)
с двумя элементами, которые вы хотите синхронизировать. Я обнаружил, что решение pawel имеет проблемы с продолжением медленной прокрутки после того, как мышь или трекпад были фактически выполнены с помощью операции.
См. рабочий пример здесь.
// 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);
}
}
Ответ 4
Работает как часы (см. 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);
});
Ответ 5
Мне нравится чистое решение для pawel, но ему не хватает чего-то, что мне нужно, и у него странная ошибка прокрутки, где он продолжает прокручиваться, а мой плагин будет работать с несколькими контейнерами не только двумя.
http://www.xtf.dk/2015/12/jquery-plugin-synchronize-scroll.html
Пример и демонстрация: http://trunk.xtf.dk/Project/ScrollSync/
Плагин: http://trunk.xtf.dk/Project/ScrollSync/jquery.scrollSync.js
$( 'прокручивать') scrollSync();.
Ответ 6
Если вы не хотите пропорциональную прокрутку, а скорее прокрутите равное количество пикселей в каждом поле, вы можете добавить значение изменения к текущему значению поля, к которому вы привязываете событие scroll-event.
Скажем, что #left
- небольшое поле, а #right
- большее поле.
var oldRst = 0;
$('#right').on('scroll', function () {
l = $('#left');
var lst = l.scrollTop();
var rst = $(this).scrollTop();
l.scrollTop(lst+(rst-oldRst)); // <-- like this
oldRst = rst;
});
https://jsfiddle.net/vuvgc0a8/1/
Добавив значение изменения, а не просто установив его равным #right
scrollTop()
, вы можете прокручивать вверх или вниз в маленьком поле, независимо от того, что его scrollTop()
меньше, чем большее поле. Примером этого является страница пользователя на Facebook.
Это то, что мне нужно, когда я приехал сюда, поэтому я решил поделиться с ним.
Ответ 7
Из решения pawel (первый ответ).
Для хронологической синхронизации прокрутки с помощью jQuery это решение:
var $divs = $('#div1, #div2'); //only 2 divs
var sync = function(e){
var $other = $divs.not(this).off('scroll');
var other = $other.get(0);
var percentage = this.scrollLeft / (this.scrollWidth - this.offsetWidth);
other.scrollLeft = percentage * (other.scrollWidth - other.offsetWidth);
setTimeout( function(){ $other.on('scroll', sync ); },10);
}
$divs.on('scroll', sync);
JSFiddle
Другим решением для нескольких горизонтально синхронизированных divs является это, но оно работает для div с одинаковой шириной.
var $divs = $('#div1, #div2, #div3'); //multiple divs
var sync = function (e) {
var me = $(this);
var $other = $divs.not(me).off('scroll');
$divs.not(me).each(function (index) {
$(this).scrollLeft(me.scrollLeft());
});
setTimeout(function () {
$other.on('scroll', sync);
}, 10);
}
$divs.on('scroll', sync);
NB: Только для разделов с одинаковой шириной
JSFiddle