モーダルウィンドウ時に背景固定でスクロールさせない方法を調べて実装しました。
Contents
モーダルウィンドウ時に背景固定でスクロールさせない
よくモーダルウィンドウを開いた際に、背景固定でスクロールさせない方が良い場合があります。
軽く調べたところ2つのプラグインがでてきました。
Body Scroll Lock(body-scroll-lock)とv scroll lockの評判
ダウンロード数や更新日をみるとBody Scroll Lockがよさそうです。
評判はよいようです。
body-scroll-lockむちゃ便利…!https://t.co/jLY7KN6YJh
— masa5714 (@b95oss) March 9, 2020
今までこんなの書いて、background.addFixed(); とかで動かしてた…!今までアホなことしてましたわ!! pic.twitter.com/Ph5BXAYsf5
少し気になります。
これ使ってるライブラリ側の問題ぽい(body-scroll-lock)
— yona (@yonah6g) March 9, 2021
スマホとPCで挙動が違うみたい https://t.co/3UtN3ZanIk
iOS の Safari問題
スクロールさせない実装は他にもありますが、Safari問題があるようです。
Safariにはbody-scroll-lockを使いましょう!https://t.co/15pbhNTV6C
— たかもそ/Web Developer (@takamosoo) October 26, 2021
body-scroll-lock というライブラリに頼りました。100vh が iOS の Safari で狂う問題については、window が resize されるたびに <html> タグの style に –vh: ~px; という変数を持たせて、CSS側で動的に 100vh の高さを偽装しているという実装になっております!
— 若月佑樹 / メンテモ (@Qrymy) June 18, 2019
実装!vueでBody Scroll Lockの使い方:vue(nuxt)で背景をスクロールさせない)
nuxtの場合、mountedに指定します。インポートして次のようなコードを書くと画面が固まります。
mounted() {
this.modal = document.querySelector('.window')
disableBodyScroll(this.modal)
}
実際にはモーダルウィンドウだけ子コンポーネント化して親子のやりとりが必要そうです。でないと固まってしまうので。
<script>
import { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock'
export default {
data() {
return {
modal: null
}
},
mounted() {
this.modal = document.querySelector('.window')
disableBodyScroll(this.modal)
},
beforeDestory() {
clearAllBodyScrollLocks()
},
methods: {
closeModal() {
enableBodyScroll(this.modal)
this.$emit('close')
}
}
}
</script>
ちゃんと再読み込みしないと反映されないようなので要注意。
vueでモーダルウィンドウにおけるabsoluteとfixedの違い
モーダルウィンドウを開いたとき、よく黒背景を画面全体に敷きます。
しかしabsoluteを使うとコンポーネントのテンプレート単位のabsoluteになってしまうようです。要はコンポーネントの単位でしか暗くなりません。というわけでこうなりました。
.modal-background {
/* position: absolute; */
position: fixed;
top: 0;
left: 0;
z-index: 1;
width: 100%;
height: 100%;
cursor: pointer;
background-color: rgba(0, 0, 0, 0.7);
}
モーダルウィンドウのz-index
ちょっとよく考えた方がよいかもですね。
ちなみに、Bootstrap の各 z-index の値は、
— 石鷹/ishitaka (@xakuro_com) May 4, 2020
1: デフォルト
2: ホバー
3: アクティブ状態
1000: ドロップダウン
1020: スティッキー
1030: 上下部の固定
1040: モーダル背景
1050: モーダル
1060: ポップオーバー
1070: ツールチップ
こんな感じ。
1071以上で勝てる!#CSS
ep.51 Vue 3 Study「Teleport でキレイに解消するモーダルの z-index 問題」 | UIT INSIDE https://t.co/aoby9Ie2qc #UIT_INSIDE
— potato4d/Takuma HANATANI (@potato4d) June 26, 2020
というわけで、 Vue 3 の Teleport について紹介しています。Beta の人は使えるほか、portal-vue だと 2.x でもほぼ同じ感覚で使えます。
- 重ねあわせたい要素にはすべてpositionが入っていないと重なり順がおかしくなる場合があります。z-indexとpositionはセットで使うでしょうか。
- 親要素がz-indexを持つ場合、子要素のz-indexは順番が正しく表示されない場合があります。親要素管理のz-indexのため。
要素にpositionが定義されているか、z-indexの番号が正しい順序であるか、を確認する。
子要素のz-indexレベルを制限する親要素がないことを確認する。
https://coliss.com/articles/build-websites/operation/css/4-reasons-z-index-isnt-working.html
https://www.codegrid.net/articles/z-index-1/
.z-4-6
のz-indexの値が6だからといって、.z-5
の上には配置されません。
【解決済】iPhoneでモーダルウィンドウ閉開時にスクロールしてトップに戻る問題【ios12で検証】
しかし、iosの問題はこれだけでは解決しません。
固定する問題とモーダルウィンドウ が開いたとき、
スクロールしてトップに戻る問題は別問題のようです。
最初、body-scroll-lockで全部解決するものかと😓
試したところ、body-scroll-lockでは後者は解決しません。
モーダルウィンドウ が開いたとき、bodyにスタイルを付与します。
document.body.style.top = -${window.scrollY}px;
this.scrollValue = document.body.style.top
閉じるときは少々複雑です。
loses its scroll position
https://css-tricks.com/prevent-page-scrolling-when-a-modal-is-open/
開いたときに座標を失われています。つまり再取得した上で元に戻す必要があるようです。
closeModal() {
this.preventScrollTop()
enableBodyScroll(this.modal)
},
preventScrollTop() {
// const scrollY = document.body.style.top //なぜか取得できない場合がある。。
const scrollY = this.scrollValue
document.body.style.top = ''
window.scrollTo(0, parseInt(scrollY || '0') * -1)
}
今回、解決に貢献した記事は引用先にあるおなじみのcss-tricksさんです。ありがとうございます。
ただ、css-tricksさんの記事そのままではうまくいかず結局一工夫しました。scrollYがうまく取得できない場合(取得できる場合もある)があり、そのため最終的に変数で持たせるようにしました。
また最初モーダルが開いたときの黒背景をひきました。この黒背景をクリックしても閉じれます。この黒背景が親コンポーネントにあったのですが、黒背景をクリックして閉じたときスクロール(データのやりとりをしても)したため、全部子コンポーネントに入れました。
この作業は1番めんどくさかったです。。難しいというより面倒なだけ😓
Body Scroll Lockのエラー
ターゲットの指定が間違っていただけでした。凡ミスです。
bodyScrollLock.esm.js:180 disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.
classの指定でドットが抜けているとでるようです。
this.modal = document.querySelector('window')
またこのエラーはwatchで監視するととなぜかこのエラーがでましたね。mountedはでません。おそらく何かしらおかしな使い方をするとでるのかもしれません。
モーダルウィンドウ時に背景固定でスクロールさせない(CSS制御による別の方法)
以前、試した別の方法も覚書程度に残しておきます。ポイントは次のコードです。
touch-action: none;
https://benfrain.com/preventing-body-scroll-for-modals-in-ios/
引用先の議論を参考にしました。
あとはJavascrpt側からbodyタグにclassの追加と削除をしてあげます。開くタイミングと閉じるタイミングでコールすればいいだけです。
document.body.classList.remove('modal-active')
document.body.classList.remove('modal-active')
modal-activeのclassでtouch-action: none;をしているわけです。
ただ、iPhoneでスクロールしてトップに戻る問題は別問題ですので、そちらの対応も必要です。対応方法は上記のとおりです。
モーダルウィンドウとは(メリット/デメリット/使いどころ)
モーダルウィンドウとポップアップの違い
モーダルウィンドウとポップアップは同じような意味で使っている人もいますが、モーダルウィンドウは固定でプログラミング的には黒背景をひくことにより他のボタンを押せなくしています。
ポップアップは他の操作もできるものですかね。ゲームとかで閉じずに他の操作が可能で横に移動しておくものもありますよね。
モーダルウィンドウは中央位置で開くことが多いが、違うものの場合、スクロール時にポップアップを隅っこで開いてもボタンが押せて戻れなくてはいけない。
モーダルウィンドウのデメリット・メリット
- メリットは画面遷移がいらない。
- 普通のウィンドウ(ページ送りがついたポップアップ等もある)の場合、面積が決まるので情報量が少ない場合が多い。
- エラー、警告、ロード、はい・いいえの確認などで使える。
- 個人的にやめてほしいモーダルウィンドウはセールスや広告。あれだけは無意味。。。毎回でてくるアプリは最悪のユーザビリティでしょう。広告は主張せずユーザーが主体的にクリックしたいとき意外でしゃばってはいけません。謙虚な姿勢が大事。
参考になれば幸いです。
コメント