モバイル端末におけるwindow.scrollTo()とUserAgentの罠に立ち向かうお話
「ページを開いたときに特定の位置に自動スクロールさせたい」、「ページの最上部にスクロールして戻るためのボタンが欲しい」といった自動スクロールに関する要望はよく挙がるのではないかと思います。
今回はそんな自動スクロールをスマートフォン向けアプリのWebViewで動作させるときに行った対応に関して取り上げていきます。
目次
はじめに
こんにちは。クラウドソリューショングループのtakinami.sです。
この記事は アイソルート Advent Calendar 2021 21日目の記事です。
スクロールさせるには
JavaScriptで絶対位置でスクロールさせるときには
window.scrollTo()
を用います。
実際に記述する際には
window.scrollTo(0, 100)
上記のようにX軸方向とY軸方向のスクロール位置を指定するか、もしくは
window.sctollTo({ top: 100, left: 0, behavior: 'smooth' })
上記のようにX軸方向(left)とY軸方向(top)のスクロール位置に加え、behaviorでスクロールするときの振る舞い(アニメーションの有無)を指定します。
検討した方針
スクロールするときのアニメーションを設定する方法は、iOS14未満やAndroid5.0未満などOSのバージョンによっては対応してない場合がありました。
そのため、UserAgentに含まれているOSの種類やバージョンによって条件分岐させようと考えました。
※OSのバージョンによる対応状況は「Can I use…」というページを参考にしました。window.scrollTo()の対応状況はこちら。
UserAgentを見て対応する
iOS
iOSであればUserAgentに【Version】と【Mobile】、【Safari】という項目が連続して含まれているのが特徴です。
具体的には以下のような形になります。
Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1
まず、上記した特徴からiOSの端末であることを判別します。
次に、Versionという文字列の直後にある数字がOSのバージョンにあたるので、ここを見て14未満ならスクロールのアニメーションをつけないよう条件分岐をさせれば良いわけです。
コードは以下のようになります。
// UserAgentを取得 let browserAgent = navigator.userAgent; // iOS端末のバージョンを取得する let matchSafari = browserAgent.match(/Version\/([0-9_]+).*Mobile.*Safari.*/); let safariVersion = parseFloat(matchSafari ? matchSafari[1]: 0); // アニメーションの有無の分岐 if (safariVersion >= 14) { window.scrollTo({ top: 100, left: 0, behavior: "smooth" }); } else { window.scrollTo(0, 100); }
Android
AndroidであればUserAgentに【Android】という文字列が含まれます。
具体的には以下のような形になります。
Mozilla/5.0 (Linux; Android 12; SM-A102U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.104 Mobile Safari/537.36
【Android】という文字列の直後にある数字がOSのバージョンにあたるので、5.0未満であればスクロールのアニメーションをつけないよう条件分岐をさせます。
コードは以下のようになります。
// UserAgentを取得 let browserAgent = navigator.userAgent; // Android端末のバージョンを取得する let matchAndroid = browserAgent.match(/Android\s([0-9]*)/i); let androidVersion = parseFloat(matchAndroid ? matchAndroid[1] : 0); // アニメーションの有無の分岐 if (androidVersion >= 5.0) { window.scrollTo({ top: 100, left: 0, behavior: "smooth" }); } else { window.scrollTo(0, 100); }
しかし、Android5.0以上なのにアニメーション付きのスクロールをしてくれない端末がありました。
Galaxyシリーズの端末です。
Galaxyシリーズにはサムスン独自のブラウザであるSamsung Internet Browserが搭載されているため、UserAgentの別の項目を見て判別しなくてはなりません。
罠1:Samsung Internet Browser
Samsung Internet Browserには【SamsungBrowser】という文字列が含まれます。
具体的には以下のような形になります。
Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-A515F) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/15.0 Chrome/90.0.4430.210 Mobile Safari/537.36
【SamsungBrowser】という文字列の直後にある数字がブラウザのバージョンにあたるので、8.2未満スクロールのアニメーションをつけないよう条件分岐をさせます。
コードは以下のようになります。
// UserAgentを取得 let browserAgent = navigator.userAgent; // SamsungBrowserのバージョンを取得する let matchSamsung = browserAgent.match(/SamsungBrowser\/([0-9]+)/); let samsungVersion = parseFloat(matchSamsung ? matchSamsung[1] : 0); // アニメーションの有無の分岐 if (samsungVersion >= 8.2) { window.scrollTo({ top: 100 left: 0, behavior: "smooth" }); } else { window.scrollTo(0, 100); }
この対応を行ってもスクロールが動作しないGalaxy端末がありました。
罠2: スクロールが動作しないGalaxy端末
この事象はAndroid6.0.1を搭載したGALAXY S5で確認しました。
原因としては搭載しているSamsung Internet BrowserがUserAgentにAndroidのバージョンは含まれているものの、【SamsungBrowser】という文字列が含まれていなかったため、Galaxyシリーズの端末であると判別できないためでした。
つまり該当する端末に搭載されているSamsung Internet BrowserのUserAgentがルールに則っていないことになります。
ルールの定義が厳しく決められているわけではないようなので、仕方がないのかもしれませんが。
このような場合はUserAgentを見ての対応は難しくなるため、心苦しいですが該当端末のスクロールの設定は行わないことにします。
おわりに
アプリ開発やWEBページの作成を行う際に、OSの旧バージョンをどのあたりまでサポートするかは悩む部分であると思います。
もし少し古いバージョンもサポートするように決まった場合は、今回取り上げたような想定外の事象があり得るということを頭の片隅においていただけると幸いです。