Android標準ブラウザでscrollTopが即座に更新されない
Posted: Updated:
window.scrollByを実行した直後
スムーズスクロール系の処理が,Androidで動かないという話があったのでデバッグしました.
どうにも,Android 2.xにおける標準ブラウザではwindow.scrollBy
を実行した後に,document.body.scrollTop
がすぐには更新されないみたいでした.20〜50msぐらいのタイマーを挟んであげると意図した値が取得できる感じ.
検証コード
大体こんな感じで検証できるんじゃないかと.
$(function() { $('#button').click(function() { var $self = $(this); window.scrollBy(0, -document.body.scrollTop); $self.after('<p>直後に取得: '+document.body.scrollTop+'</p>'); setTimeout(function() { $self.after('<p>タイマ取得: '+document.body.scrollTop+'</p>') }, 50); }); });
<div style="width: 640px; height: 2048px; background: gray;">はこ</div>
<button id="button">一番上にスクロール</button>
scrollTopを使用した判定処理は遅延させてしまう
今回の修正対象になった処理は,いわゆる"ページの一番上に戻るボタン"でした・・・.lol
実際に修正した処理のサンプル
scrollTopの参照が必要な判定処理をタイマー的に遅らせました.本来は,タイマー処理内で即座に評価していたのを,次のタイマー処理の冒頭に遅延させることで対応しています.
/**
* 指定座標までスクロールする
* @param {Number} x
* @param {Number} y
* @parem {Number} m 移動間隔
* @param {Number} k 移動量
* @param {Function} callback
*/
function scrollToXy( x, y, m, k, callback ) {
var lazyEvaluator;
setTimeout(function () {
if (lazyEvaluator && lazyEvaluator()) {
if ( 'function' == typeof(callback) ) {
callback();
}
return;
}
var left = document.body.scrollLeft;
var top = document.body.scrollTop;
var h = Math.floor((-1 * (left - x) * k));
var v = Math.floor((-1 * (top - y) * k));
window.scrollBy(h, v);
// スクロール位置の評価は、つぎのタイマー処理に遅延させる
// Android2.x〜3.xの標準ブラウザで、document.body.scrollXxxの更新が遅い
// scrollBy直後に参照すると、更新されていないため条件を正しく評価できない
lazyEvaluator = function () {
return ((h == 0) || ((left + h) != document.body.scrollLeft))
&& ((v == 0) || ((top + v) != document.body.scrollTop));
};
setTimeout(arguments.callee, m);
return false;
}, m);
};
変更前
参考までに変更前はこんな感じ.タイマー処理でscrollByを繰り返すシンプルな処理です.
function oldScrollToXy( x, y, m, k, callback ) {
setTimeout(function () {
var left = document.body.scrollLeft;
var top = document.body.scrollTop;
var h = Math.floor((-1 * (left - x) * k));
var v = Math.floor((-1 * (top - y) * k));
window.scrollBy(h, v);
if ( 1
&& ((h == 0) || ((left + h) != document.body.scrollLeft))
&& ((v == 0) || ((top + v) != document.body.scrollTop))
) {
if ( 'function' == typeof(callback) ) {
callback();
}
return true;
}
setTimeout(arguments.callee, m);
return false;
}, m);
};