公開日時:2022.07.11

最終更新日:2022/07/11

Vue.js(3)で複数設置可能なモーダルウインドウを作ろう!

アイキャッチ

こんにちは!
最近なぜか足がつりそうなGlobal Web Designの福田です(笑)

前回、モーダルウインドウを実装しよう!(記事はこちら)という記事で、単体のモーダルウインドウを作成しました。
モーダルウインドウは単体だと簡単に作ることができますが、複数のモーダルウインドウを作るときはHTMLにdata属性を書いて、そのdata属性をJavaScriptで監視するという操作をしなければなりません。

監視するという操作はHTMLやJavaScriptにコードを追加するためコードが長くなってしまいます。

そこで今回はVue.jsのコンポーネントという仕組みを使い、複数対応のモーダルウインドウを作成したいと思います。

モーダルウインドウを作る

モーダルウインドウのは前回の記事のものをかりて、作りたいと思います。

<button id="modalOpen">モーダルを開く</button>
<div class="modal-bg">
  <div class="modal">
    <div class="modal-content">
      <p>モーダルの内容</p>
      <a class="modalClose">モーダルを閉じる</a>
      <div class="close-btn">
        <div>
          <span></span>
          <span></span>
        </div>
      </div>
    </div>
  </div>
</div>
button{
  width: 300px;
  height: 50px;
  line-height: 50px;
  background-color: #2675BC;
  color: #fff;
  border: transparent;
  transition: .5s;
  cursor: pointer;
}
button:hover{
  opacity: .5;
}
.modal-bg{
  position: fixed;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, .6);
  top: 0;
  left: 0;
  z-index: 10;
}
.modal{
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.modal-content{
  width: 400px;
  height: 200px;
  background-color: #fff;
  position: relative;
  padding: 40px;
  box-sizing: border-box;
}
.modal-content>p{
  text-align: center;
  margin-bottom: 20px;
}
.modalClose{
  display: block;
  text-align: center;
  color: #2675BC;
  transition: .5s;
}
.modalClose:hover{
  opacity: .5;
}
.close-btn{
  position: absolute;
  right: 0;
  top: -50px;
  cursor: pointer;
}
.close-btn>div{
  width: 30px;
  height: 30px;
  position: relative;
}
.close-btn>div>span{
  width: 100%;
  height: 1px;
  background-color: #fff;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}
.close-btn>div>span:first-of-type{
  transform: rotate(45deg);
}
.close-btn>div>span:last-of-type{
  transform: rotate(-45deg);
}
.modal-bg.active{
  display: block;
}

ブラウザで見てみると、

まだVue.jsを書いていないので、モーダルウインドウが表示されています。
ここからVue.jsを使ってモーダルウインドウを操作します。

Vue.jsでの操作

Vue.jsでのモーダルウインドウの操作は

  1. data関数を定義
  2. v-showを使ってモーダルウインドウを非表示
  3. v-on:clickを使ってbuttonタグを押すとモーダルウインドウを表示
  4. 閉じるボタンと✖️を押して、モーダルウインドウを閉じる
  5. Vue.jsのトランジションを使ってモーダルウインドウにアニメーションをつける

になります。

data関数を定義

まずはVue.jsでdata関数を定義します。
HTMLのbodyの閉じタグの前にVue.jsを読み込みます。

<script src="https://unpkg.com/vue@next"></script>

そしてVue.jsでdata関数を書きます。

const modal = Vue.createApp({
  data() {
    return {
      modalWindow: false,
    }
  },
})
modal.mount('#modal')

今回は「modal」というIDにしました。
モーダルウインドウは非表示にしたいのでdata関数を「modalWindow: false,」 としました。

HTMLに「#modal」を指定したいのでdivタグでまとめます。

<div id="modal">
  <button id="modalOpen">モーダルを開く</button>
  <div class="modal-bg">
    <div class="modal">
      <div class="modal-content">
        <p>モーダルの内容</p>
        <a class="modalClose">モーダルを閉じる</a>
        <div class="close-btn">
          <div>
            <span></span>
            <span></span>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

divタグに「id=”modal”」を書いてVue.jsを使う準備ができました。

v-showを使ってモーダルウインドウを非表示

次に「v-show」を使ってモーダルウインドウを非表示にさせます。
「v-show」は条件付きレンダリングでCSSに「display: none;」をかけた振る舞いになります。

今回はdata関数の「modalWindow」が「true」の時にモーダルウインドウを表示、「modalWindow」が「false」の時に非表示にしたいので、HTMLを

<div id="modal">
  <button id="modalOpen">モーダルを開く</button>
  <!-- v-showを追加 -->
  <div class="modal-bg" v-show="modalWindow">
    <div class="modal">
      <div class="modal-content">
        <p>モーダルの内容</p>
        <a class="modalClose">モーダルを閉じる</a>
        <div class="close-btn">
          <div>
            <span></span>
            <span></span>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

と書きます。
ブラウザで見てみると

モーダルウインドウを消すことができました。

v-showは今回の場合「modalWindow: false」であれば「display: none;」に「modalWindow: true」であれば表示というのをたったの1行で書くことができます。

v-on:clickを使ってbuttonタグを押すとモーダルウインドウを表示

次に「v-on:click」を使ってbuttonタグを押すとモーダルウインドウを表示の機能を追加します。

「v-on:click」はクリックした時にイベントを発火時のJavaScriptの実行を可能にします。
「v-on:click」は省略して「@click」と書くことができるのでHTMLを

<div id="modal">
  <!-- v-on:click(@click)を追加 -->
  <button id="modalOpen" @click="modalWindow = true">モーダルを開く</button>
  <div class="modal-bg" v-show="modalWindow">
    <div class="modal">
      <div class="modal-content">
        <p>モーダルの内容</p>
        <a class="modalClose">モーダルを閉じる</a>
        <div class="close-btn">
          <div>
            <span></span>
            <span></span>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

のようにします。

「@click=”modalWindow = true”」でbuttonタグを押したらdata関数が「modalWindow: true」に変わります。
ブラウザで見てみると

クリックしたらモーダルウインドウが表示されました。

閉じるボタンと✖️を押して、モーダルウインドウを閉じる

閉じるボタンと✖️を押して、モーダルウインドウを閉じる動きを実装します。

今回は閉じるボタンと✖️に「v-on:click」を使って「modalWindow: false」にします。

<div id="modal">
  <button id="modalOpen" @click="modalWindow = true">モーダルを開く</button>
  <div class="modal-bg" v-show="modalWindow">
    <div class="modal">
      <div class="modal-content">
        <p>モーダルの内容</p>
        <!-- v-on:click(@click)を追加 -->
        <a class="modalClose" @click="modalWindow = false">モーダルを閉じる</a>
        <!-- v-on:click(@click)を追加 -->
        <div class="close-btn" @click="modalWindow = false">
          <div>
            <span></span>
            <span></span>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

閉じるボタンと✖️に「@click=”modalWindow = false”」を追加しました。
ブラウザで見てみると

「@click=”modalWindow = false”」をつけた部分をクリックするとモーダルウインドウを閉じることができました。

vue.jsのトランジションを使ってモーダルウインドウにアニメーションをつける

今のままではクリックしたときにモーダルウインドウが一瞬で表示、非表示になるため、Vue.jsのトランジションを使ってモーダルウインドウにアニメーションをつけます。

Vue.jsのtransitionラッパーコンポーネントを使いアニメーションさせていきます。
HTMLを

<div id="modal">
  <button id="modalOpen" @click="modalWindow = true">モーダルを開く</button>
  <!-- transitionラッパーコンポーネントを追記 -->
  <transition name="fade">
    <div class="modal-bg" v-show="modalWindow">
      <div class="modal">
        <div class="modal-content">
          <p>モーダルの内容</p>
          <a class="modalClose" @click="modalWindow = false">モーダルを閉じる</a>
          <div class="close-btn" @click="modalWindow = false">
            <div>
              <span></span>
              <span></span>
            </div>
          </div>
        </div>
      </div>
    </div>
  </transition>
</div>

CSSを

button{
  width: 300px;
  height: 50px;
  line-height: 50px;
  background-color: #2675BC;
  color: #fff;
  border: transparent;
  transition: .5s;
  cursor: pointer;
}
button:hover{
  opacity: .5;
}
.modal-bg{
  position: fixed;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, .6);
  top: 0;
  left: 0;
  z-index: 10;
}
.modal{
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.modal-content{
  width: 400px;
  height: 200px;
  background-color: #fff;
  position: relative;
  padding: 40px;
  box-sizing: border-box;
}
.modal-content>p{
  text-align: center;
  margin-bottom: 20px;
}
.modalClose{
  display: block;
  text-align: center;
  color: #2675BC;
  transition: .5s;
}
.modalClose:hover{
  opacity: .5;
}
.close-btn{
  position: absolute;
  right: 0;
  top: -50px;
  cursor: pointer;
}
.close-btn>div{
  width: 30px;
  height: 30px;
  position: relative;
}
.close-btn>div>span{
  width: 100%;
  height: 1px;
  background-color: #fff;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}
.close-btn>div>span:first-of-type{
  transform: rotate(45deg);
}
.close-btn>div>span:last-of-type{
  transform: rotate(-45deg);
}
.modal-bg.active{
  display: block;
}
/* transition 追記*/
.fade-enter-active, .fade-leave-active{
  animation-name: fade;
  animation-duration: .5s;
}
.fade-leave-active{
  animation-direction: reverse;
}
@keyframes fade{
  0%{
    opacity: 0;
  }
  100%{
    opacity: 1;
  }
}

のように書きます。
今回はCSSアニメーション(CSSでアニメーションを実装しよう!の記事はこちら)を使ってフェードのアニメーションを実装しました。

Vue.jsではtransitionラッパーコンポーネントを使ったときにトランジションクラスが適用されます。

  1. v-enter: enter の開始状態。要素が挿入される前に適用され、要素が挿入された 1 フレーム後に削除されます。
  2. v-enter-active: enter の活性状態。トランジションに入るフェーズ中に適用されます。要素が挿入される前に追加され、トランジション/アニメーションが終了すると削除されます。このクラスは、トランジションの開始に対して、期間、遅延、およびイージングカーブを定義するために使用できます。
  3. v-enter-toバージョン 2.1.8 以降でのみ利用可能です。 enter の終了状態。要素が挿入された 1 フレーム後に追加され (同時に v-enter が削除されます)、トランジション/アニメーションが終了すると削除されます。
  4. v-leave: leave の開始状態。トランジションの終了がトリガされるとき、直ちに追加され、1フレーム後に削除されます。
  5. v-leave-active: leave の活性状態。トランジションが終わるフェーズ中に適用されます。leave トランジションがトリガされるとき、直ちに追加され、トランジション/アニメーションが終了すると削除されます。このクラスは、トランジションの終了に対して、期間、遅延、およびイージングカーブを定義するために使用できます。
  6. v-leave-toバージョン 2.1.8 以降でのみ利用可能です。 leave の終了状態。leave トランジションがトリガされた 1 フレーム後に追加され (同時に v-leave が削除されます)、トランジション/アニメーションが終了すると削除されます。
引用: https://jp.vuejs.org/v2/guide/transitions.html
引用: https://jp.vuejs.org/v2/guide/transitions.html

検証ツールで確認すると

今回はtransitionラッパーコンポーネントに「name=”fade”」としましたので、クリックするとモーダルウインドウが表示されるときは「fade-enter-active」「fade-enter-to」がクラスに描画され、モーダルウインドウを非表示にするときは「fade-leave-active」「fade-leave-to」が描画されているのがわかります。

コンポーネントを使って複数のモーダルウインドウを実装

Vue.jsを使って単体のモーダルウインドウを実装してきましたが、Vue.jsコンポーネントを使って複数のモーダルウインドウを実装していきます。

先ほど書いたHTMLをコンポーネントに登録します。

const modal = Vue.createApp({})
// 追記
modal.component('modal-window',{
  template:`
  <div id="modal">
    <button id="modalOpen" @click="modalWindow = true">モーダルを開く</button>
    <transition name="fade">
      <div class="modal-bg" v-show="modalWindow">
        <div class="modal">
          <div class="modal-content">
            <p>モーダルの内容</p>
            <a class="modalClose" @click="modalWindow = false">モーダルを閉じる</a>
            <div class="close-btn" @click="modalWindow = false">
              <div>
                <span></span>
                <span></span>
              </div>
            </div>
          </div>
        </div>
      </div>
    </transition>
  </div>
  `,
  data() {
    return {
      modalWindow: false,
    }
  },
})
modal.mount('#modal')

今回はコンポーネントの名前を「modal-window」としました。
「modal.component(‘modal-window’,{})」でコンポーネントを登録し、「template: “」にテンプレートを入れます。

「template: “」はシングルクオーテーション(”)ではなく、グレーブアクセント(“)で書いています。
これでコンポーネントを登録できたのでHTMLを

<div id="modal">
  <modal-window></modal-window>
</div>

と書くと

コンポーネントに登録した内容がHTMLに描画されています。

スロットを使う

このまま「<modal-window></modal-window>」とかけば複数のモーダルウインドウを描画できますが、文字が全て一緒なので「モーダル1を開く」「モーダル2を開く」とそれぞれのモーダルウインドウのテキストを変更します。

Vue.jsではスロットを使って要素のコンテンツの受け渡しができます。
今回は開く時のbuttonタグと、pタグのモーダルの内容、モーダルを閉じるのコンテンツをスロットで受け渡します。

const modal = Vue.createApp({})
modal.component('modal-window',{
  template:`
  <button id="modalOpen" @click="modalWindow = true">
    <slot name="modal-btn"></slot>
  </button>
  <transition name="fade">
    <div class="modal-bg" v-show="modalWindow">
      <div class="modal">
        <div class="modal-content">
          <p>
            <slot name="modal-content"></slot>
          </p>
          <a class="modalClose" @click="modalWindow = false">
            <slot name="modal-close"></slot>
          </a>
          <div class="close-btn" @click="modalWindow = false">
            <div>
              <span></span>
              <span></span>
            </div>
          </div>
        </div>
      </div>
    </div>
  </transition>
  `,
  data() {
    return {
      modalWindow: false,
    }
  },
})
modal.mount('#modal')

「<slot></slot>」を使ってスロットを登録できます。
「<slot></slot>」の「name=””」で決めた名前をHTMLに書いてあげて描写します。

buttonタグの中に「<slot name=”modal-btn”></slot>」、pタグの中に「<slot name=”modal-content”></slot>」、閉じるaタグの中に「<slot name=”modal-close”></slot>」としてコンテンツの受け渡し口を作り、HTMLに

<div id="modal">
  <modal-window>
    <template v-slot:modal-btn>モーダル1を開く</template>
    <template v-slot:modal-content>モーダル1の内容</template>
    <template v-slot:modal-close>モーダル1を閉じる</template>
  </modal-window>
</div>

描画されないtemplateタグを使い、「v-slot:」の後ろにJavaScriptでつけたスロットネームを書くとHTMLに描画されます。

後はtemplateタグのコンテンツを変えるだけで複数のモーダルウインドウを実装できます。

<div id="modal">
  <modal-window>
    <template v-slot:modal-btn>モーダル1を開く</template>
    <template v-slot:modal-content>モーダル1の内容</template>
    <template v-slot:modal-close>モーダル1を閉じる</template>
  </modal-window>
  <modal-window>
    <template v-slot:modal-btn>モーダル2を開く</template>
    <template v-slot:modal-content>モーダル2の内容</template>
    <template v-slot:modal-close>モーダル2を閉じる</template>
  </modal-window>
  <modal-window>
    <template v-slot:modal-btn>モーダル3を開く</template>
    <template v-slot:modal-content>モーダル3の内容</template>
    <template v-slot:modal-close>モーダル3を閉じる</template>
  </modal-window>
  <modal-window>
    <template v-slot:modal-btn>モーダル4を開く</template>
    <template v-slot:modal-content>モーダル4の内容</template>
    <template v-slot:modal-close>モーダル4を閉じる</template>
  </modal-window>
</div>

完成はこちら。

See the Pen Untitled by Fukuda Yuzuru (@fukuda-yuzuru) on CodePen.

まとめ

いかがでしたでしょうか?
今回はVue.js(3)で複数設置可能なモーダルウインドウをご紹介しました。

Vue.jsはコンポーネントを登録してパーツを使いまわせた、スロットを使って受け渡しができたりと非常に便利です。
Vue.jsの書き方はHTMLのインラインで書いたり、Vue.jsの独自の書き方がありますが覚えてしまえば、簡単に書くことができます。

この記事をキッカケにVue.jsを使いたいなと思っていただけると嬉しいです!

この記事を書いた人
福田 弦

福田 弦

2020年にGlobal Web Desingを立ち上げ。得意分野は、コーディング、CMS構築、ディレクション。現在はブログ、マーケティングなどを勉強中。趣味はドラム。

SNSでシェア