JavaScriptでスマホの画面の向きを変更(回転)した時のイベント検知にはrisizeイベントが良く使われると思います。
当サイトでもスマホのサイドバーの高さを算出するためにrisizeを使っているのですが、たまに画面の高さがうまく取れない時があることに気付きました。
どうやらaddEventListenerにrisizeを使うと、タイミングの問題でスマホ回転前の画面の高さを拾うことがあるようです。こんな時の対処方法についてです。
今回はrisizeでしたが、orientationchangeでも同じ問題が起こると思います。
この記事の目次
risizeイベントでスマホ回転時の高さが取れない時がある
スマートフォンの「縦向きの画面」と「横向きの画面」の両方に臨機応変に対応するために、JavaScriptを使って画面サイズを算出するのは良くあることだと思います。
例えばスマホの画面サイズを算出するのに以下のようなコードが使われます。
function setHeight() { let vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh}px`); } setHeight(); window.addEventListener('resize',setHeight); ~省略~ $('#id').on('click',function(){ $('#hoge').css({'height':'calc(var(--vh, 1vh) * 100)','width':'100vw'}); });
addEventListener
にrisizeイベントを指定し、画面サイズを監視することでスマホの画面の回転も検知して、任意の処理を行う感じです。
今回コードはどうでも良いのですが、CSSのcalcなどと組み合わせるとビューポートいっぱいの高さを取って使うことが出来たりで便利です。
当サイトではサイドバーの閉じるボタンの領域を、先ほどのようなコードでスマホの画面サイズまで広げることで、メニュー以外を押しても閉じれるようにしています。
スマホの画面が横向きの時は上の画像のようになります。
そして「横向き」から「縦向き」に回転させると下の画像のようになります。
スマホの画面回転時にはrisizeイベントが発火するので、問題なく閉じるボタンの領域も縦長になります。ここまでは想定通りの動作です。
で、ここからが本題です。
画面回転を繰り返すとたまに高さが取れない
ある日たまたま実機のスマホでメニューを開いたまま、画面を回転させたところ以下のような状態になりました...。
本来は閉じるボタンの領域がスマホの画面いっぱいまで広がるはずが中途半端な領域になってしまっています。
ここでスマホの画面を「横向き」→「縦向き」→「横向き」...と回転を繰り返してみると、10回に1度くらいの頻度で画面の高さがうまく取れないという感じでした。
ナニコレ!?
原因はJavaScriptの非同期処理っぽい
検証を繰り返したところ、どうやらスマホの画面サイズが取れていないのではなく、以前の画面サイズである横向き時の高さが算出されているようです。
というのも画面回転とresizeイベントの検知には若干のタイムラグが発生することがあるらしく、必ずしも「画面回転完了→resizeイベント発火→画面サイズの再計算」という流れにはならないようです。
つまり、スマホの画面の回転が完了する前にresizeイベントが発火してしまった場合、window.innerHeight
で取った値には以前の画面モードの値が入り、これがそのまま計算に使われるということです。え~
今回たまたまこの問題を発見できましたが、よく考えればJavaScriptは非同期処理が主体なので、必ずしも回転完了後に計算が行われるという保証はないのですねぇ( ;∀;)
対処法:setTimeout等で同期処理に変更する
対処方法ですが、簡単で確実なのがsetTimeout()
を使う方法でした。
これを使って画面回転時は少し待ってから、画面サイズの計算を行うように変更したところうまくいきました。
window.addEventListener('resize',setHeight);
このように書いていたところを、
window.addEventListener('resize',function(){ setTimeout(function(){ setHeight(); },100); });
という感じにすればOKです。
今回は100ミリ秒に設定してみましたが、これで画面回転をいくら繰り返しても回転後の画面の高さが取れました。なお待ち時間が短すぎると頻度は落ちるものの同じ問題が発生する可能性があるのでご注意を。
また、CSSで何かを変更する時には100ミリ秒の時間差が発生します。見た目の変更が遅れるのがイヤならアニメーションなどを活用すれば誤魔化せると思います...。
setTimeoutが簡単ですが、JavaScriptに精通しているなら他のメソッドで同期処理にしてあげても大丈夫だと思います。とにかく画面サイズの計算をresizeイベント発火後に行うようにうまくやって下さい。
まとめ
以上、JavaScriptのresizeでスマホの画面回転時の画面の高さがうまく取れなかった時の内容と対処方法でした。
今回スマホの実機で問題が発覚した後、パソコンのChromeのデベロッパーツールで画面の回転を何度かシュミュレートしたのですが、この問題は発生しませんでした。
スマホのOSやブラウザに関係なく発生してしまう問題だと思われますが、実機でないと発生しない問題なのかもしれません…偶然ですが見つけられてよかったです・w・