修改div后,后续的onscroll事件是否会被取消吗?

时间:2019-07-18 03:30:02

标签: javascript jquery

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

  • beforeDiv:这是一个空的spacer div。该div中的行位于视口的可见部分上方,因此我们不需要渲染它们
  • middleDiv:此div将具有已渲染的行
  • afterDiv:这是一个空的spacer 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>

1 个答案:

答案 0 :(得分:1)

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