公開日時: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でのモーダルウインドウの操作は
- data関数を定義
- v-showを使ってモーダルウインドウを非表示
- v-on:clickを使ってbuttonタグを押すとモーダルウインドウを表示
- 閉じるボタンと✖️を押して、モーダルウインドウを閉じる
- 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ラッパーコンポーネントを使ったときにトランジションクラスが適用されます。
v-enter
: enter の開始状態。要素が挿入される前に適用され、要素が挿入された 1 フレーム後に削除されます。v-enter-active
: enter の活性状態。トランジションに入るフェーズ中に適用されます。要素が挿入される前に追加され、トランジション/アニメーションが終了すると削除されます。このクラスは、トランジションの開始に対して、期間、遅延、およびイージングカーブを定義するために使用できます。v-enter-to
: バージョン 2.1.8 以降でのみ利用可能です。 enter の終了状態。要素が挿入された 1 フレーム後に追加され (同時にv-enter
が削除されます)、トランジション/アニメーションが終了すると削除されます。v-leave
: leave の開始状態。トランジションの終了がトリガされるとき、直ちに追加され、1フレーム後に削除されます。v-leave-active
: leave の活性状態。トランジションが終わるフェーズ中に適用されます。leave トランジションがトリガされるとき、直ちに追加され、トランジション/アニメーションが終了すると削除されます。このクラスは、トランジションの終了に対して、期間、遅延、およびイージングカーブを定義するために使用できます。v-leave-to
: バージョン 2.1.8 以降でのみ利用可能です。 leave の終了状態。leave トランジションがトリガされた 1 フレーム後に追加され (同時にv-leave
が削除されます)、トランジション/アニメーションが終了すると削除されます。
検証ツールで確認すると
今回は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を使いたいなと思っていただけると嬉しいです!