次のコードで2つのDIV
の同期スクロールを実装しようとしています。
$(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;
}
このコードでは、2つの問題に直面しています。
1)divのサイズが異なるため、スクロールがうまく同期しません。これは、scrollTop
の値を直接設定しているためです。スクロールされたコンテンツのパーセンテージを見つけて、他のscrollTop
の対応するdiv
値を計算する必要があります。実際の高さと現在のスクロール位置を見つける方法はわかりません。
2)この問題はfirefox
でのみ発生します。 Firefoxでは、他のブラウザのようにスクロールがスムーズではありません。これは、上記のコードがスクロールイベントの無限ループを作成しているためだと思います。なぜこれがFirefoxでのみ発生しているのかはわかりません。この問題を解決できるように、スクロールイベントのソースを見つける方法はありますか?.
どんな助けでも大歓迎です。
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);
時計仕掛けのように動作します( [〜#〜] 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);
});
これは私が使っているものです。同期する2つの要素を指定して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);
}
}
Divのサイズが同じ場合、以下のコードは、divを同期的にスクロールする簡単な方法です。
_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;
}
}
_
ここでは水平スクロールを使用していますが、代わりにscrollTopを使用できます。この関数はdivのscroll
イベントで呼び出されるため、e
はイベントオブジェクトにアクセスできます。次に、この行に適用するdivの対応するサイズの比率を単純に計算できます$('.scroll_class')[i].scrollLeft = scrollLeft;
比例スクロールではなく、各フィールドで同じ量のピクセルをスクロールする場合は、スクロールイベントをバインドしているフィールドの現在の値に、変更の値を追加できます。
_#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のユーザーページです。
これは私がここに来たときに必要だったので、共有したいと思いました。
私はパウェルのクリーンソリューションが好きですが、必要なものが欠けており、スクロールが継続し、プラグインが2つだけでなく複数のコンテナーで動作するという奇妙なスクロールバグがあります。
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
$( '。scrollable')。scrollSync();
スクロールパーセンテージを固定小数点表記percent.toFixed(0)
にパラメーターとして0を設定することにより、同期スクロールループの問題を解決しました。これにより、常に互いに追いつくことを試みている2つの同期された要素間で、部分的なスクロールの高さが一致しなくなります。このコードを使用すると、せいぜい1つの追加ステップの後に追いつくことができます(つまり、ユーザーがスクロールを停止した後、2番目の要素が追加のピクセルをスクロールし続ける可能性があります)。完璧なソリューションや最も洗練されたものではありませんが、確かに私が見つけることができる最も簡単なものです。
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);
});
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);
水平方向に同期された複数のdivの別の解決策はこれですが、同じ幅の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:同じ幅のdivのみ