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); };