Help us understand the problem. What is going on with this article?

iPhoneでもBootstrap4のモーダルを使いたい

More than 1 year has passed since last update.

今回記事を担当しているのは、新米プログラマーその2👩です!
Nuxt.jsやBootstrap4を使い始めて約2ヶ月😇
初心者ながら、ぶつかった壁を乗り越えたので書き残しておきます。

何が起こったか

Bootstrap4のモーダルがiPhoneでうまく動かない。
何かと言うと、

  • モーダルの背景がスイッとスクロールされてしまう!
  • 肝心のモーダルは止まっちゃったりガタガタとスクロールされたりする!

モーダルの縦の長さが画面の縦サイズより大きくなったとき、とても困ります😡
ちなみに、「iOSでモーダルの背景が動いちゃう〜><」という現象はよくあるみたいです。なんだと。

iOSでモーダルが上手くスクロールできない問題への対応
iOS Safari で絶対位置指定された要素のスクロールが出来ない件
iphone – ModalでスクロールするとSafariで背景がスクロールする

...などなど

原因わからず

「Safariがダメ」「GoogleChromeがダメ」「iPhoneだけがダメ」…etc.
色々と原因は挙げられていましたが、サイトによっては他のサイトと真逆のことを言っていることもありました。
はっきりした原因はわからないまま、ひとまず日本語で書かれた解決法は試しました。全滅。
英語で書かれているものも読みました。全滅。
この時点でBootstrap4のモーダルは諦めました。

BootstrapVueへ

引っ越しました。
モーダルやらプルダウンやら使ってみましたが、「Vue.js向けのBootstrap4!便利!」という印象です。
Bootstrap4はイベントをjQueryで指定しなきゃいけないことがあるんですね。そう、モーダルとかね。
対してBootstrapVueでは@イベントが用意されています!
というわけで、がんばって英語を読んで実装しました。

BootstrapVue公式ドキュメント

インストール

こちらを参考に。

Bootstrap を Vue.js で使ってみよう

イベントでカスタマイズ

ページとコンポーネントに分けて実装しました。
この時点でカスタマイズしたのは、フッターのボタンと、それに対するclickイベントです。
フッターについては、<footer slot="modal-footer"></footer>でサクッと変更できます😊

components/modal.vue
<template>
  <div>
    <!-- モーダルを表示するボタン クリックされたらモーダルを表示する関数を呼ぶ -->
    <b-button @click="showModal">
      open!
    </b-button>
    <!-- モーダル部分 タイトルはここで指定する -->
    <b-modal ref="cardNameModal" title="CARD NAME">
      <!-- ボディ部分に表示 -->
      <div class="d-block text-center">
        <h3>白魔道士ピケル</h3>
      </div>
      <!-- フッター部分をカスタマイズ -->
      <footer slot="modal-footer">
        <!-- クリックされたらモーダルを閉じる関数を呼ぶ -->
        <b-button variant="secondary" @click="hideModal">
          閉じる
        </b-button>
        <!-- クリックされたらモーダルを閉じる関数を呼ぶ -->
        <b-button variant="primary" @click="hideModal">
          完了
        </b-button>
      </footer>
    </b-modal>
  </div>
</template>

<script>
export default {
  methods: {
    // モーダルを表示する関数
    showModal() {
      this.$refs.cardNameModal.show()
    },
    // モーダルを閉じる関数
    hideModal() {
      this.$refs.cardNameModal.hide()
    }
  }
}
</script>
pages/index.vue
<template>
  <div>
    <modalComponent></modalComponent>
  </div>
</template>

<script>
import modalComponent from '~/components/modal.vue'

export default {
  components: {
    modalComponent
  }
}
</script>

これをベースにイベントを追加し、スクロールを固定していきます。
必要なのは次の2点。

  1. モーダルを表示するとき、背景のスクロールを固定する -> showイベント
  2. モーダルを閉じるとき、背景のスクロール固定を解除する -> hiddenイベント

これでカスタマイズして完ぺ…ちょっと待ったー!!
ここで運命の分かれ道!ファイルの構成を確認します!

  1. ページとコンポーネントに分けず、1つのファイルに書いている
  2. ページとコンポーネントに分けている

1の場合は、そのままページに対してスクロールを固定すればOKです。
クラスのバインディングがスマートな書き方だと思います😉

Vue.js公式 クラスとスタイルのバインディング

今回は2のパターンな訳ですが…
他にもモーダルを使う部分が出てくるとなると、その度にスクロール固定のクラスを書かなくてはいけません。
そして、スクロールを固定しなくてはならないファイルに一つ一つ指定するのも大変…。

というわけで、今回はscssjQueryを使ってスクロールを固定・解除する方法を取りました!
(Nuxt.jsを使っている以上、できる限りjQueryは避けたいところですが妥協しました。)

components/modal.vue
<template>
  <div>
    <!-- モーダルを表示するボタン クリックされたらモーダルを表示する関数を呼ぶ -->
    <b-button @click="showModal">
      open!
    </b-button>
    <!-- モーダル部分 タイトルはここで指定する -->
    <!-- show,close,hiddenイベントを追加 -->
    <b-modal
      ref="cardNameModal"
      title="CARD NAME"
      @show="showModal"
      @close="hideModal"
      @hidden="hideModal">
      <!-- ボディ部分に表示 -->
      <div class="d-block text-center">
        <h3>白魔道士ピケル</h3>
      </div>
      <!-- フッター部分をカスタマイズ -->
      <footer slot="modal-footer">
        <!-- クリックされたらモーダルを閉じる関数を呼ぶ -->
        <b-button variant="secondary" @click="hideModal">
          閉じる
        </b-button>
        <!-- クリックされたらモーダルを閉じる関数を呼ぶ -->
        <b-button variant="primary" @click="hideModal">
          完了
        </b-button>
      </footer>
    </b-modal>
  </div>
</template>

<script>
export default {
  methods: {
    // モーダルを表示する関数
    showModal() {
      this.$refs.cardNameModal.show()
      // スクロールを固定するクラスを追加する
      $('body').addClass('showing_modal')
    },
    // モーダルを閉じる関数
    hideModal() {
      this.$refs.cardNameModal.hide()
      // スクロールを固定するクラスを解除する
      $('body').removeClass('showing_modal')
    }
  }
}
</script>
pages/index.vue
<template>
  <div>
    <modalComponent></modalComponent>
  </div>
</template>

<script>
import modalComponent from '~/components/modal.vue'

export default {
  components: {
    modalComponent
  }
}
</script>
assets/scss/custom.scss
// モーダルを表示したとき、背景のスクロールを固定する
.showing_modal {
  position: fixed;
  overflow: hidden;
}

@import 'bootstrap/scss/bootstrap.scss';
@import 'bootstrap-vue/src/index.scss';

scss部分に関してはこちらを参考にしています😉

iOSでモーダルが上手くスクロールできない問題への対応

これでiPhoneでもモーダルのスクロールに悩まされなくなりました!
あとはバシバシお好みの見た目にカスタマイズしましょ〜

hiddenイベントについて

個人的に面白いな、と思ったのがhiddenイベント
どこで役立ったかというと、「モーダルの背景をクリックする」という場合。

BootstrapVue「モーダルの背景をクリックされたら閉じてあげる。スクロール固定のクラス?知らないよ😆」

…つまり、「モーダルを閉じたイベント」に対してスクロール固定を解除する必要が出てくるわけです。
そのイベントこそがhiddenイベント!
他のイベントと同じようにクラス解除の指定をしてあげるだけで無事解決です。

まとめ

Vue.jsやNuxt.jsとともにBootstrap4を使って慣れてきたら、ぜひBootstrapVueも使ってみてください。
初心者でもある程度使われた方なら、きっと壁を超える方法が見つかります😊

ka_nabell_dev
TCGレボリューションの開発担当😎 DECK MAKER、プライの開発を担当しています🌟 東海一の技術者集団を目指してチームで活動しています。
https://tcg-revo.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away