“onscroll”是在重绘前还是后触发的?

7

我有一个带滚动条的div元素。

在许多浏览器上(我在MacOS和Linux上测试了最新版本的Chrome和Firefox),似乎浏览器确保在滚动触发重绘之前调用绑定到onscroll的代码。

换句话说,当滚动时,以下jsfiddle示例不会闪烁或闪烁:http://jsfiddle.net/m2E65/1/

var onscroll = function() {
    var y = $("#container").scrollTop() + 30;
    var z = 0
    for (var c=0; c<y*10000; c++) {
        z+=c;
    }
    $("#label").text("bocal : "+z);
    $("#label").css("top", y);
};
$('#container').scroll(onscroll);

然而在Ubuntu上的Linux Chromium v28中,它会闪烁。几乎像我们使用setTimeout延迟onscroll一样严重(http://jsfiddle.net/m2E65/2/):

$('#container').scroll(function() {
    window.setTimeout(onscroll, 0);
});

在同一个浏览器上,即使像http://jsfiddle.net/m2E65/4/中使用requestAnimationFrame,闪烁问题仍然存在(见下文)。

var onscroll = function() {
    var y = $("#container").scrollTop() + 30;
    var z = 0
    for (var c=0; c<y*10000; c++) {
        z+=c;
    }
    $("#label").text("bocal : "+z);
    $("#label").css("top", y);
    window.requestAnimationFrame(onscroll);
};
window.requestAnimationFrame(onscroll);

我的问题是:

  • 这个有规范吗?
  • 有没有办法确保在所有浏览器上,代码在重绘之前运行?
  • 额外加分:这种行为有多少罕见?

你在Ubuntu下是否遇到过其他网站的闪烁问题?这可能是由于你的显卡垂直同步(vsync)问题引起的。 - benathon
闪烁是指有时我会看到文本在不同的位置(如果我向上,则更低)出现1帧。我不认为图形卡有问题。也许是 Wayland + chromium 的组合? - fulmicoton
如果有人能给我一个规范的指针,我会很开心...我找不到任何关于onscroll/paint应该如何交互的说明。 - fulmicoton
1个回答

4

1. 从文档对象模型(DOM)级别3事件规范 W3C文档中:

当文档视图或元素已滚动时,用户代理必须分派此事件。此事件类型在滚动完成后分派。

我对这两个句子的理解是,在滚动过程完成(包括重绘)后,浏览器必须分派scroll事件。

2. 我认为没有办法确保代码在所有浏览器上都在重绘之前触发。也许您可以尝试捕获用户可以滚动的所有方式,我想到了以下几种:

  • 鼠标滚轮 -> wheel事件
  • 选择文本 -> mousedown
  • 使用滚动条 -> 您只能希望mousedown会触发。

3. 在我看来,您的第一个fiddle在闪烁:

  • Safari 7.0.1
  • iOS 7上的Safari
  • 就像你在Ubuntu上使用的Chrominum说的那样

以下浏览器不会闪烁:

  • Firefox 27.0 Mac和Ubuntu
  • Opera 19.0 Mac
  • Chrome

现在它认为特别是WebKit浏览器(除了Chrome)在调用scroll事件之前不会重新绘制,而且没有好的方法可以防止这种情况。


非常感谢您在这些浏览器上进行测试! - fulmicoton
1
对于遇到这个问题的其他人:我发现应对这个问题唯一可靠的方法是在容器上使用overflow-hidden,并使用另一组滚动条。最后,JavaScript会完成必要的粘合剂来滚动原始容器的内容。 - fulmicoton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接