webkit内核浏览器在当前页面刷新时scrollTo失效

实际上这是去年7月份时碰到的问题,但是那会儿只是单纯认为这是浏览器纪录了刷新前的位置,并自发做了回到原来位置的处理导致的失效,并没有刻意去处理,而只是简单做了如下操作:

1
2
3
4
5
6
7
$(window).on('load', function () {
setTimeout(function () {
$('html,body').animate({
'scrollTop' : top
}, 0);
}, 200);
});

上周时,因周年庆的活动,偶然间触动了这部分代码,偶然间触发了这个问题。于是今天花了几个小时做了算是精准的定位。

以下代码直接运行在html中,具体可以下载这个html代码做修改和查看这个页面

测试:chrome 41.0.2272.104 (64-bit) / ff 37.0.1

1
2
3
4
log('test 1 for window.scrollTo');
window.scrollTo(0, 2000);
log('now document.body.scrollTop is : ' + document.body.scrollTop); // chrome: 2000 FF: 0
log('now document.documentElement.scrollTop is : ' + document.documentElement.scrollTop); // chrome: 0 ff: 2000

…… 等等等等,总共做了六个测试,包括window.scorllTowindow.scrollwindow.scrollBywindow.onload then scrollTowindow.onload then setTimeout then scrollTo。最后log的结果都是类似的,而实际效果就是,chrome在刷新的情况下,怎么都到不了设定的2000px的位置,而firefox可以。重新打开倒是正常。

而实际业务逻辑中做的减法调试也耗费了不少的时间。包括以这样的代码找到所有的scroll事件依依去除。

1
2
3
4
5
6
var lookForBindHandler = function (dom, eventType) {
$.each( $._data(dom).events[eventType], function (key, handle) {
console.log(handle.handler.toString());
});
};
lookForBindHandler(window, 'scroll')

到依次去除业务代码、依赖到的库文件lazyload、 infinitescroll、jQuery 到最后直接用原生的 window.scrollTo(jQuey内部用的应该也是window.scrollTo无误)。

1
2
3
4
5
6
7
8
if ( win ) {
win.scrollTo(
top ? val : jQuery( win ).scrollLeft(),
top ? val : jQuery( win ).scrollTop()
);
} else {
elem[ method ] = val;
}

另见stackoverflow,链接A链接B,定位为Chrome 刷新当前页面的情况。

后做继续跟进,发现Safari、Opera(偶尔) 也有此现象,而IE、FF都是正常,那推测为Webkit的通病。

对应的UA为:

1
2
3
4
Chrome 	Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.104 Safari/537.36
Opera Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36 OPR/27.0.1689.69
Safari Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.5.17 (KHTML, like Gecko) Version/8.0.5 Safari/600.5.17
FireFox Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:37.0) Gecko/20100101 Firefox/37.0

因此,只需做webKit和是否刷新页面的判断,就可以提供比原来多200ms以上的用户反馈和体验。

1
2
3
4
5
6
7
8
9
10
11
12
if (window.navigator.userAgent.search(/AppleWebKit\/(\d+\.)+(\d+)? \(KHTML\, like Gecko\)/) !== -1) {
if (window.name) { // 默认如果被设置过 就采用延时
$(window).on('load', function () {
setTimeout(next, 200);
});
} else {
window.name = 'refresh_frag';
next();
}
} else {
next();
}

网页是否当前页面刷新的判断 - 根据window.name

其实我们能做的可以做的还有很多…
The End.
2015.04.22 01:15