【问题标题】:How can I prevent scroll position jump after a freeze during a heavy render/calculations?在繁重的渲染/计算过程中,如何防止在冻结后滚动位置跳跃?
【发布时间】:2019-12-26 20:56:38
【问题描述】:

当我在大量渲染期间滚动某些列表(例如 50 个 Highchart 项目)时,我会出现滚动冻结(这很好)。问题是在加载所有图表后,滚动(被冻结)“跳跃”到列表的末尾(向上或底部,取决于滚动方向)。有没有办法在所有重渲染完成后从滚动位置继续滚动,比如 30%?

我尝试为列表元素添加样式,在重渲染开始之前移除滚动,如“溢出:隐藏”,然后在所有渲染结束后将“溢出:滚动”添加回元素。但这无济于事,仍然会经历“跳跃”。

我尝试过的看起来是这样的:

// inner-container is a class name of all scrollable elements on the page that I need to control
// content-wrapper is a list inside of the container, position for that I want to be consistent after a render (without "jumps")

const elements = document.getElementsByClassName("inner-container");
window.elCoords = [];
for (let el of elements) {
   el.style.overflowY = 'hidden';
   el.style.pointerEvents = 'none';
   const coords = el.getElementsByClassName('content-wrapper')[0].getBoundingClientRect();
   window.elCoords.push(coords.y)
}

渲染结束后,我删除“溢出:隐藏”样式,以便元素能够再次滚动并设置滚动位置,就像在重渲染开始之前一样:

const elements = document.getElementsByClassName("inner-container");
let index = 0;
for (let el of elements) {
   const inner = el.getElementsByClassName('content-wrapper')[0];
   inner.scrollTo({
      top: window.elCoords[index],
      behavior: 'smooth'
   });
   index += 1;

   el.style.overflowY = 'scroll';
   el.style.pointerEvents = 'all';
}

实际:重渲染结束后,滚动位置跳转到可滚动容器的结束/开始

预期:重渲染结束后,滚动应该从重渲染开始之前的位置继续滚动

【问题讨论】:

    标签: javascript html css google-chrome browser


    【解决方案1】:

    浏览器是单线程的。当您的计算正在运行时,UI 事件实际上并没有被阻止,而是排队(滚动事件、鼠标事件、键盘事件等)。当可以继续执行时,队列会被刷新,因此任何发生的滚动以及任何其他事件都会向前播放。

    在开始计算之前,您可以阻止页面滚动,并在计算完成后再次允许滚动:

    function preventDefault(event) {
      event.preventDefault();
    }
    
    document.addEventListener('scroll', preventDefault);
    // and other events ('wheel', DOMMouseScroll', etc.)
    // Heavy rendering...
    // ...
    document.removeEventListener('scroll', preventDefault);
    

    或:

    window.onscroll = function () { window.scrollTo(0, 0); };
    // Heavy rendering...
    window.onscroll = null;
    

    【讨论】:

    • 感谢您的想法。我会尝试并让你知道这是否有效。如果解决了问题,我会接受答案
    • 由于被动事件性质,preventDefault 似乎不再适用于滚动。
    • 实际上,您必须禁用许多不同的滚动方法才能真正阻止所有滚动。请参阅涵盖它们的这个问题和答案stackoverflow.com/questions/4770025/…。我已经更新了我的答案以提供一个更简单的案例,这也应该可以解决问题。
    猜你喜欢
    • 2020-12-24
    • 2014-03-26
    • 2019-06-18
    • 2014-10-23
    • 1970-01-01
    • 1970-01-01
    • 2018-04-27
    • 2010-12-14
    • 2022-08-18
    相关资源
    最近更新 更多