修改 div 时,后续的滚动事件会被取消吗?



我正在尝试创建一个虚拟滚动机制,以便我可以为大型列表保持 DOM 轻量级。这个想法是主div 中不可见的内容(可见视口之前/之后的内容)可以从 dom 中删除并替换为用作间隔符的空div。有一个带有滚动条的div,它包含 3 个div:

  • beforeDiv :这是一个空的间隔div.此div 中的行位于视口的可见部分上方,因此我们不需要渲染它们
  • middleDiv :此div 将具有呈现的行
  • afterDiv:这是一个空的间隔div.这个div 中的行位于视口的可见部分下方,因此我们不需要渲染它们

代码片段显示了包含 1000 行的虚拟滚动。这是我的真实代码的最小示例来证明这一点。每 10 行被视为一个"页面",每行硬编码为 100px 高度。我有一个 onscroll 处理程序,它将检查我是否需要更新 3 个div 以在div 之前/之后调整大小并更改在中间div 中呈现的行。当我们向下滚动时,之前div 的高度会增长,之后div 的高度会缩小(这两个div 都是空的,主要是间距div,因此滚动条可以正常工作)。

这就是问题所在。当我按向下箭头一次滚动时,它实际上会多次触发 onscroll 事件以实现平滑滚动(运行代码段并按向下箭头一次 - 您将看到滚动顶部一次更改几次几个像素)。那很好。但是当我到达"页面"边界并调整之前/中间/之后的div 时,它似乎取消了后续滚动。因此,当我到达边界时,它不会滚动全部数量,并且看起来非常生涩。假设该事件应该以 1px、3px、5px、6px、10px 触发 5 次,总共 25px。它只会在 1px 上触发一次,随后的滚动会被取消。我猜是因为我修改了div的内容?

如果按住向下箭头,它相当平滑,但您仍然可以看到每 10 行略有延迟。

任何想法如何使滚动更流畅?

$( document ).ready(function() {
var ROW_HEIGHT = 100;
var PAGE_COUNT = 10;
var data = [];

for(var i = 0; i < 1000; i++){
data[data.length] = "index " + i;
}

function min(n,min){
return n < min? min:n;
}

function log(msg){
var logger=$("#logger")[0];
$(logger).append("<div>"+msg+"</div>")
logger.scrollTop = logger.scrollHeight;
}

var scrollInfo = {};

function virtualScroll(){
var scrollTop = $("#scrollbarWrapper").scrollTop();
log("scrollTop:"+scrollTop);
var topRow = Math.floor(scrollTop/ROW_HEIGHT);
var topPageRow = topRow-topRow%PAGE_COUNT;
var beforeRows = min(topPageRow-PAGE_COUNT,0);
var middleRows = 30;
var afterRows = data.length - middleRows - beforeRows;
if (scrollInfo != null && (scrollInfo.beforeRows == beforeRows && scrollInfo.middleRows == middleRows && scrollInfo.afterRows == afterRows )){
return;
}

log("=====ADJUSTING=DIVS=====");
scrollInfo = {beforeRows:beforeRows,middleRows:middleRows,afterRows:afterRows};
$(".middle").empty();

for (var i = beforeRows; i < beforeRows+middleRows; i++){
$(".middle").append("<div class='row'>"+data[i]+"</div>");
}

$(".before").outerHeight(beforeRows*ROW_HEIGHT);
$(".after").outerHeight(afterRows*ROW_HEIGHT);
$("#scrollbarWrapper").scrollTop(scrollTop);
$(".middle").focus();
}

virtualScroll();
$("#scrollbarWrapper").on("scroll",virtualScroll);
});
#scrollbarWrapper{
display:inline-block;
width:500px;
height:500px;
overflow:auto;
border:1px solid black;
}
.row{
display:inline-block;
height:100px;
width:100%;
border:1px solid black;
box-sizing:border-box;
}
#logger{
display:inline-block;
height:500px;
/*width:500px;*/
overflow:auto;
border:1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="scrollbarWrapper" class="scrollbarWrapper">
<div class="before" tabindex="0"></div>
<div class="middle" tabindex="0"></div>
<div class="after" tabindex="0"></div>
</div>
<div id="logger"></div>

所以我发现任何修改滚动顶部的东西都会停止滚动惯性。我重新设计了整个组件,这样我就可以避免设置滚动顶部。相反,我只是更新之前和之后的div,以确保视口中的内容正确保留在视口中。我还发现,当我在 dom 中添加滚动顶部上方的任何内容时,它会导致滚动顶部发生变化,这就是为什么我在示例代码中有一行来调整滚动顶部的原因。现在我按照正确的顺序做,在附加新内容之前,我在div 之前缩小。

最新更新