※ iOS 8での挙動に関連して追加情報を書いてみました: Safari for iOS 8での画面回転とイベント
はじめに
モバイルブラウザ(iOS SafariやAndroid標準ブラウザ、Chrome for Androidなど)で、WebアプリのJavaScriptから端末画面が縦長(portrait)と横長(landscape)のどちらを向いているかを調べる方法は、Web上で検索すると色々出てくるのですが、特にAndroid対応を考えると、例えば「au開発者情報Blog: CSS3 MediaQueries で端末回転方向によるレイアウト切り替えができない」辺りに書いてあるように、やってみると上手くいかないことが色々と出てきたりするように思えます。
注意事項(参考)
厳密には、window.orientation
やorientationchange
イベントは、現在は*あくまでもベンダ独自仕様扱い*として整理されていることに注意が必要です。現在ではChrome, Opera, FirefoxがW3C仕様(ドラフト)のScreen Orientation APIをサポートし、Edgeも古いドラフト仕様のScreen Orientationをサポートしていますので、iOSに限りorientationchange
を利用する等の対応の方が適切かもしれません。
何が厄介なのか
悩ましいポイントとしては、例えばこんなところでしょうか。
-
orientationchange
イベント、resize
イベント、実際の画面リサイズ処理が実行される順序がiOS SafariとAndroid向けブラウザとで異なっている。 - Androidの場合、フォームの文字列入力にフォーカスする等によってソフトウェアキーボードが表示されるときにもリサイズが発生してしまい、縦長画面にもかかわらず横長と判定できてしまうケースがある。
- Androidの場合、
window.orientation
で取得できる値は、端末の実装上、縦長と横長のどちらを「正面」として扱っているかによって異なっている。 - Android 2.1以前の標準ブラウザではそもそも
orientationchange
イベントもwindow.orientation
プロパティも対応していない。
ひとまず、(初代Xperia等には申し訳ないですが)4.の対応はここでは扱わないこととして、それ以外について考えてみます。
では、どうするか
まず、1.についてですが、
- iOSの場合は、画面リサイズが完了してから、
orientationchange
やresize
といったイベントが発生する。 - Androidの場合は、
orientationchange
イベント→画面リサイズ→resize
イベント、といった順序で処理が行われる。
という流れとなっているため、画面の向きの判定はresize
イベントのリスナー関数で行えばよい、ということはよく知られています。
ただ、この時にウィンドウの幅と高さ(window.innerWidth
とwindow.innerHeight
、あるいはscreen.width
とscreen.height
)を比較する方法で判別してしまうと、2.の問題にぶつかってしまいます。
では、幅と高さの比較ではなく、window.orientation
を使おう...と考えるわけですが、今度は3.の問題にぶつかってしまいます。
そこで、Webアプリの実装で、初期化処理が完了する前にはソフトウェアキーボードが表示されないようにフォーム等を細工しておく、という前提で、次のような対処を考えてみます。
- 初期化処理(
window
のload
イベントのリスナ)において、画面の幅・高さと、window.orientation
の値がどのように対応しているかをチェックする。 - 以後、
resize
イベント発生時に、画面の向きの検出処理を行う。
具体的には - サンプルコード
サンプルコード(orientation.js)を次に示します。
var defaultOrientation; // window.orientationが0または180の時に縦長であればtrue
// 初期化処理
window.addEventListener('load', function() {
if('orientation' in window) {
var o1 = (window.innerWidth < window.innerHeight);
var o2 = (window.orientation % 180 == 0);
defaultOrientation = (o1 && o2) || !(o1 || o2);
checkOrientation();
}
// もしあれば、その他Webアプリの初期化処理
}, false);
// 画面回転時に向きをチェック
window.addEventListener('resize', checkOrientation, false);
function checkOrientation () {
if('orientation' in window) {
// defaultOrientationがtrueの場合、window.orientationが0か180の時は縦長
// defaultOrientationがfalseの場合、window.orientationが-90か90の時は縦長
var o = (window.orientation % 180 == 0);
if((o && defaultOrientation) || !(o || defaultOrientation)) {
// ここに縦長画面への切り替え処理を記述
console.log('portrait');
}
else {
// ここに横長画面への切り替え処理を記述
console.log('landscape');
}
}
}
ちなみに、今後の動向について
現在、W3Cでは、画面の向きを把握・制御する新しい仕様として、The Screen Orientation APIの仕様策定が進められています。
現状のwindow.orientation
プロパティは、実際の画面の向き(縦長/横長)の判定には少々使いにくいくくなっていますが、新しいScreen Orientation APIでは、デバイス上でプライマリの画面の向きがportraitとlandscapeのどちらに定められているかを踏まえて、画面の向きを検出できるようになっています。また、画面の向きをロック/アンロックするメソッドも新たに定義されています。
この新しいAPIは、window
ではなくscreen
オブジェクトに対する拡張となっている点に注意が必要です。
なお、この仕様は、現時点ではベンダプレフィクス(moz
, ms
)付きでFirefox for AndroidとIE11でサポートされているとのことです(参考: MDN)。