始めに
メニューなどでクリックしたらリストがドロップダウンで表示することがあると思いますが、その時関係のない場所をクリックしたら閉じるという機能をどう実装するかという話です。
(デザインが適当すぎるせいで伝わっているか不安・・・)
今回はVue.jsで実装する場合の説明をしたいと思います。
2020/04/16追記
色々記事を見ていたらどうやらvue-click-outside
というライブラリがあるようで、これを使った場合は以下のような実装をしなくて良かったそうです><
https://www.npmjs.com/package/vue-click-outside
なので、もし自前で実装する場合はどうするのかを知りたい方だけ続きを見てくださいm(_ _)m
実装方法
1. 全体をクリックしたときに閉じるようにする
まずは関係のない場所をクリックしてもイベントが拾えるようにwindowからイベントリスナーを拾います。
addEventListenerで登録してコンポーネント破棄時にイベントを消さないといけないのでthis._onBlurHandler
という変数にメソッドを保存しています。
new Vue.extend({
mounted() {
// windowにイベントリスナーをセットする
window.addEventListener('click', this._onBlurHandler = (event) => {
this.$data.isOpen = false; // 表示フラグをOFFにする
});
},
beforeDestroy() {
// コンポーネントが破棄されるタイミングにイベントリスナーも消す
window.removeEventListener('click', this._onBlurHandler);
}
});
2. 自分の要素に含まれない場所をクリックしたときだけ閉じる
上記のやり方でも閉じてはくれますが、ドロップダウンの中身をクリックしても閉じてしまいます。
ドロップダウンの中身をクリックしたときは何もせず、それ以外の場所をクリックしたときだけ閉じるようにする場合は、Elementにあるcontains
メソッドを使ってチェックします。
具体的にはevent.target
がイベントの元となったエレメントになっており、refで適当な場所を設定して、そのElementでcontains
メソッドを呼んで含まれているかのチェックをしています。
window.addEventListener('click', this._onBlurHandler = (event) => {
// targetがコンポーネントの中に含まれているものなら何もしない
if (this.$refs.elRoot.contains(event.target)) {
return;
}
this.$data.isOpen = false;
});
終わりに
関係のない場所をクリックしたときに閉じるという処理は意外とちゃんとやろうと思うと中々上手くいかなかったので記事にまとめました。
サンプルコードは以下に置きましたので興味がある方は確認してみてください。
See the Pen ドロップダウン by wintyo (@wintyo) on CodePen.
余談
ちなみにこのやり方をする前は閉じたくない要素にstopPropagation
を呼んでwindowまでイベントが送られないようにしていました。ただこれすると他のドロップダウンをクリックしたときにそれ以外のドロップダウン要素が閉じてくれなかったりで色々難点がありました・・・。