一生懸命書いて保存ボタンを押したら、404エラーが出て消えてしまった…ㅠㅠ
モバイルウェブ開発で経験した2つの厄介なクロスブラウジングの問題と、その他考慮すべき問題について
ブラウザ自体のアドレスバーとナビゲーションバーによるビューポート高さ計算の問題
SafariとSamsung Internetは、ブラウザ自体に下部のナビゲーションバーがある。show/hideを特に設定していない限り、スクロールダウンすると表示され、スクロールアップすると消える。
問題は、これらによってビューポートが固定されず、変化することだ。
例えば、高さを100vhにした場合、ナビゲーションバーがないときはレイアウトが正確に描画されるが、ナビゲーションバーが表示されると、ナビゲーションバーがUIを隠してしまう問題が発生する。
dvhは動的ビューポートで、ビューポートの高さが変更されるたびに変化を自動的に反映する。そのため、アドレスバーやボトムナビゲーションの有無によるUIレイアウトの変更に対応できる。
svhは、アドレスバーやナビゲーションを除いた純粋なUI領域の高さである。アドレスバーやナビゲーションが表示されると予想される状況では、svhを使用するのが正確だ。
inputを選択した時の仮想キーボードによるUIずれの問題
モバイルSafariでinputを選択すると、仮想キーボードによって既存のUI要素がずれる問題が発生した。他のブラウザでは一般的に仮想キーボードが絶対的に表示されるが、Safariモバイルウェブでは、まるでdisplay blockを適用したかのように場所を占めていた。
Safariでは、仮想キーボードが表示されるときにビューポートの高さが動的に調整され、vh単位を使用する要素が再調整されることで発生する問題である。これを解決するには、
html, body {
height: 100%;
min-height: 100vh; /* 固定された最小高さを設定 */
overflow: hidden;
}
html, body {
height: -webkit-fill-available; /* Safari対応 */
}
Safariなど特定のブラウザで適用される属性を挿入する。
3. JavaScriptでビューポートを動的に制御する。
window.addEventListener('resize', () => {
if (window.innerHeight < 500) {
// キーボードが表示された時に実行するコード
document.body.classList.add('keyboard-visible');
} else {
// キーボードが消えた時に実行するコード
document.body.classList.remove('keyboard-visible');
}
});
resizeイベントを掛けてビューポートが変わるのを検知し、適切なスタイルを適用する。
3-1. focusinイベント発生時に位置を制御する。
document.querySelectorAll('input, textarea').forEach(element => {
element.addEventListener('focusin', () => {
setTimeout(() => {
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 100);
});
});
3-2. 仮想キーボードが表示された時に位置を制御する。(下記例ではclass適用)
let originalHeight = window.innerHeight;
window.addEventListener('resize', () => {
if (window.innerHeight < originalHeight) {
// 仮想キーボードが表示されている状態
document.body.classList.add('keyboard-visible');
} else {
// 仮想キーボードが閉じている状態
document.body.classList.remove('keyboard-visible');
}
});
私の場合は3-1を使用して解決した。もし3-1が意図通りに動作しない場合は、まず下記コードを使用して意図通りかどうかを確認する。
window.addEventListener('focusin', () => {
setTimeout(()=> {
window.scrollTo(0,0)
}, 100);
})
その他
その他にも、Safariでモバイルウェブを開発する際に考慮すべきいくつかの事項を調べた。
1. 一部の属性を使用する際には、ベンダープレフィックスを使用する。
* ベンダープレフィックスは、ブラウザが特定のCSS属性を理解してレンダリングできるようにする接頭辞であり、ブラウザごとに異なる方法で実装されたCSSを意図通りに適用できるようにする。これにより、同じブラウザ間の整合性があり、旧バージョンのブラウザとも互換性を保つことができる。
-webkit-: Chrome、Safariなど
-moz-: Mozilla Firefox
-ms-: Internet ExplorerとEdge
-o-: Opera
ベンダープレフィックスが必要な代表的な属性は以下の通りだ。
.container {
display: -webkit-flex; /* Safari */
display: flex;
-webkit-flex-direction: row;
flex-direction: row;
}
.item {
-webkit-flex-grow: 1; /* Safari */
flex-grow: 1;
-webkit-flex-shrink: 1; /* Safari */
flex-shrink: 1;
}
.grid-container {
display: -ms-grid; /* IE11と旧バージョンブラウザ */
display: grid; /* 最新ブラウザ */
}
.sticky-element {
position: -webkit-sticky; /* Safari */
position: sticky;
}
input[type="text"],
select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
その他、SafariはLocalStorageとSessionStorageの容量を約5MBに制限しており、各タブ間でStorage領域を共有しないため注意が必要だ。
ps. 元々書いていた記事が消えてしまい、とても悲しい。私が投稿した記事は質の高い記事ではないけれど…ㅠ こまめに一時保存しなければならない。