目的
表題のとおりです。
Webサイトを作っていたらよくモーダルというものを実装する機会は多いと思います。
モーダルって普通は、背景の半透明の部分をクリックしたら閉じるけど、モーダルの中身をクリックしても閉じないですよね。例えば以下の画像のモーダルも、背景の半透明の部分や、「OK」や「✗」ボタン以外のところを押してもモーダルは閉じないです。こういうやつの実装をするときの話をします。
それをJavaScriptで実装しようとしたとき、色々やりようはあるんですけど、今回サクッとできる方法を見つけた(気がする)ので書きます。思いつきなので、ラフに書きます。
対象読者
- HTML、CSS、JavaScriptで簡単なサイトなら作ったことがある(divって何?というようなレベルは卒業してる)
- JavaScriptでモーダルを実装したことがない。またはコピペでならやったことがある
- 見よう見まねでクリックイベント(ボタンをクリックしたら●●する〜というような処理)を書いたことはあるけど、深い挙動は理解していない
TL;DR
event.stopPropagation()
中身クリックでも閉じてしまう実装の例
まずHTMLがこうなってます。
<div class="mod-modalOverlay is-hide">
<div class="mod-recommendModal">
<!-- モーダルの中身 -->
</div>
</div>
CSSがこうなってます。TOPの決め打ちとかz-indexとか色々と適当なのは勘弁してください。
方針としては、is-hideクラスの付替えで表示、非表示をやります。上記HTMLを見たら、最初外側のmod-modalOverlayにis-hideクラスがついていることがわかります。なので、ページを開いたタイミングではモーダルはdisplauy: noneがついているので非表示です。
.is-hide {
display: none;
}
.mod-modalOverlay {
z-index: 150;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
}
.mod-recommendModal {
position: absolute;
top: 20%;
left: 5%;
width: 90%;
box-sizing: border-box;
background: #f5f5f5;
border-radius: 10px;
}
で、ページ読み込みが終わったタイミング(正確にはDOMが構築終わったときですが)に下記JavaScriptを実行しておきます。モーダルの背景(mod-modalOverlay)をクリックしたら、is-hideクラスをつけることで、display: noneにしてモーダルをまるごと非表示にしよう、という魂胆です。
$('.mod-modalOverlay').on('click', (event) => {
$('#.mod-modalOverlay').addClass('is-hide');
})
また、モーダルを表示するイベント(ボタンを押すとか、ログインしたとか)のときのイベントハンドラに下記を追加しておきます。
$('.mod-modalOverlay').removeClass('is-hide');
こうしておいたら、イベント発生時にis-hideクラスをもぎ取ることでモーダルの背景と、モーダル本体を表示できるのです。
しかし、以上の実装では、一度開いたモーダルに対して、モーダルの中身、すなわち.mod-recommendModalのエリア内をクリックすると、モーダルが閉じてしまいます。
もちろんモーダルの半透明の背景を押してもモーダルが閉じるのですが、モーダルの中身をクリックしても閉じてしまうのは余計なお世話ですよね。
ここまでの実装を下記jsfiddleにまとめているので、読んでみてください。「Push」ボタンを押したらモーダルは出るけど、中身の白い部分をクリックしたらモーダルが閉じてしまいます。
どうして中身クリックでも閉じてしまうのか?
なぜモーダルの中身をクリックしても閉じてしまうかというと、モーダルのクリックイベントが、その親要素へと伝搬されていってしまうからです。
HTMLでいうと、モーダルの背景の要素(.mod-modalOverlay)が、モーダルの中身の要素(.mod-recommendModal)より外側にあるので、中身をクリックしたときに、背景の要素のクリックイベントまで動いてしまいます。
HTMLの要素には上下関係のようなものがあって、小要素で発生したイベントは親要素へと受け継がれていくようです。報連相がしっかり行われている組織って感じがしますね。
詳しく知りたい方は、下記の記事を読んでみてください。
jQueryのclickとbindとliveとdelegateとonの違い
ということで、じゃあ中身をクリックしたときにモーダルを閉じないようにするためには、1つの考え方として、「子要素がクリックされたときは、親要素へイベントを伝搬しない」ような処理は書けないか?という発想が出てきます。
イベントの伝播を止める
ずばり、それをやるためのやり方が、冒頭に話したコードになります。
event.stopPropagation()
まるっと書くとこんな感じ。モーダルの中身のほうのクリックイベントハンドラで、stopPropagationという関数を実行します。
$('.mod-recommendModal').on('click' , event => {
event.stopPropagation();
})
参照:MDN
先程示した下記jsFiddleでも、コメントアウトしている形でコードを書いているので、コメントを外してRunしてみてください。中身をクリックしてもモーダルが閉じなくなっているはずです。
結論
event.stopPropagation
を使ったら、親要素のイベントハンドラが呼ばれることを防止できる!
ということをいいたかったです!