如何同步两个 div 的滚动位置?

我希望有2个按特定宽度(即500px)调整大小的 div,一个在另一个上面水平对齐。

顶部框应该隐藏它的滚动条,底部应该显示一个滚动条,当用户滚动时,我希望顶部框的偏移量改为底部框的值。因此,当底部 DIV 水平滚动时,顶部 DIV 也同时滚动。

如果这个过程更简单,我很乐意在 Jquery 中完成。

78616 次浏览
$('#bottom').on('scroll', function () {
$('#top').scrollTop($(this).scrollTop());
});

这里我们使用 .scrollTop(),从带滚动条的元素中获取 scrollTop值,并为其他元素设置 scrollTop,以同步它们的滚动位置: http://api.jquery.com/scrollTop

这假设底部元素的 ID 为 bottom,顶部元素的 ID 为 top

您可以使用 CSS 隐藏 top元素的滚动条:

#top {
overflow : hidden;
}

这里是一个演示: http://jsfiddle.net/sgcer/1884/

我想我从来没有真正的理由这样做,但它看起来相当酷的行动。

我一直在寻找一个双向解决方案,感谢你们所有人,你们的贡献帮助我做到了这一点:

$('#cells').on('scroll', function () {
$('#hours').scrollTop($(this).scrollTop());
$('#days').scrollLeft($(this).scrollLeft());});

参见 JSFiddle: https://jsfiddle.net/sgcer/1274/

希望有一天能帮到别人: -)

我知道这是条老线索,但也许能帮到别人。 如果你需要同步双向滚动,仅仅处理两个容器的滚动事件并设置滚动值是不够的,因为这样滚动事件就进入了循环,滚动不是平滑的(试着用鼠标滚轮垂直滚动,这是 Hightom 给出的一个例子)。

下面是一个如何检查滚动条是否已经同步的例子:

var isSyncingLeftScroll = false;
var isSyncingRightScroll = false;
var leftDiv = document.getElementById('left');
var rightDiv = document.getElementById('right');


leftDiv.onscroll = function() {
if (!isSyncingLeftScroll) {
isSyncingRightScroll = true;
rightDiv.scrollTop = this.scrollTop;
}
isSyncingLeftScroll = false;
}


rightDiv.onscroll = function() {
if (!isSyncingRightScroll) {
isSyncingLeftScroll = true;
leftDiv.scrollTop = this.scrollTop;
}
isSyncingRightScroll = false;
}
.container {
width: 200px;
height: 500px;
overflow-y: auto;
}


.leftContainer {
float: left;
}


.rightContainer {
float: right;
}
<div id="left" class="container leftContainer">
Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.
</div>
<div id="right" class="container rightContainer">
Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.
</div>

这是 小提琴

感谢上面的答案,我提出了一个混合的解决方案,这个方案对我来说更有效:

var isLeftScrollTopCalled = false;
$('#leftElement').scroll(function (e) {
if (isRightScrollTopCalled) {
return isRightScrollTopCalled = false;
}


$('#rightElement').scrollTop($(this).scrollTop());
isLeftScrollTopCalled = true;
});


var isRightScrollTopCalled = false;
$('#rightElement').scroll(function (e) {
if (isLeftScrollTopCalled) {
return isLeftScrollTopCalled = false;
}


$('#leftElement').scrollTop($(this).scrollTop());
isRightScrollTopCalled = true;
});

另一个解决方案,防止这个循环问题,并实现平稳滚动。 这样可以确保只有被聚焦的元素获得滚动事件。

let activePre: HTMLPreElement = null;
document.querySelectorAll(".scrollable-pre").forEach(function(pre: HTMLPreElement) {
pre.addEventListener("mouseenter", function(e: MouseEvent) {
let pre = e.target as HTMLPreElement;
activePre = pre;
});


pre.addEventListener("scroll", function(e: MouseEvent) {
let pre = e.target as HTMLPreElement;


if (activePre !== pre) {
return;
}


if (pre !== versionBasePre) {
versionBasePre.scrollTop = pre.scrollTop;
versionBasePre.scrollLeft = pre.scrollLeft;
}


if (pre !== versionCurrentPre) {
versionCurrentPre.scrollTop = pre.scrollTop;
versionCurrentPre.scrollLeft = pre.scrollLeft;
}


});
});

我注意到他的问题已经很老了,但是我认为我可以留下一个比使用计时器工作得更好的 jQuery 实现。在这里,我使用了两个事件侦听器,就像前面使用的解决方案一样,但是这将使用比例/百分比来同步两个不同大小的 div 框的滚动条。您可以应用相同的知识来获得滚动条到 Vanilla JS 解决方案的位置。这将使两个 div 之间的滚动更加平滑。

/** Scroll sync between editor and preview **/
// Locks so that only one pane is in control of scroll sync
var eScroll = false;
var pScroll = false;
// Set the listener to scroll
this.edit.on('scroll', function() {
if(eScroll) { // Lock the editor pane
eScroll = false;
return;
}
pScroll = true; // Enable the preview scroll


// Set elements to variables
let current = self.edit.get(0);
let other = self.preview.get(0);


// Calculate the position of scroll position based on percentage
let percentage = current.scrollTop / (current.scrollHeight - current.offsetHeight);


// Set the scroll position on the other pane
other.scrollTop = percentage * (other.scrollHeight - other.offsetHeight);
});
this.preview.on('scroll', function() {
if(pScroll) { // Lock the preview pane
pScroll = false;
return;
}
eScroll = true; // Enable the editor scroll


// Set elements to variables
let current = self.preview.get(0);
let other = self.edit.get(0);


// Calculate the position of scroll position based on percentage
let percentage = current.scrollTop / (current.scrollHeight - current.offsetHeight);


// Set the scroll position on the other pane
other.scrollTop = percentage * (other.scrollHeight - other.offsetHeight);


});

好的,我评估了这里提到的所有选项,它们都有这样或那样的局限性:

  • 接受的答案在使用鼠标滚轮时遇到了已知的问题。
  • 第二高的投票没有滚轮问题,但只适用于两个已知元素。如果您需要更多的元素,那么它是不可伸缩的。
  • 唯一有希望的解决方案是 拉乔的,但代码中有已知的元素引用,这些引用不包括在代码片段中。从好的方面来说,它具有性能所需的结构,而且是在 TypeScript 中。

我利用它创建了一个可重用的版本,它可以处理无限数量的元素,而且不需要 JQuery。

function scrollSync(selector) {
let active = null;
document.querySelectorAll(selector).forEach(function(element) {
element.addEventListener("mouseenter", function(e) {
active = e.target;
});


element.addEventListener("scroll", function(e) {
if (e.target !== active) return;


document.querySelectorAll(selector).forEach(function(target) {
if (active === target) return;


target.scrollTop = active.scrollTop;
target.scrollLeft = active.scrollLeft;
});
});
});
}


//RWM: Call the function on the elements you need synced.
scrollSync(".scrollSync");

您可以在这里查看 JSFiddle: http://jsfiddle.net/gLa2ndur/3。您可以看到它同时使用了水平和垂直滚动示例。

唯一已知的限制是它可能无法在不同大小的 div 上工作。我确信如果你的用例认为有必要(我的用例不需要) ,有人可以将 安德鲁的工作合并到这里。

我希望这对某些人有帮助!