LoginSignup
12
14

More than 3 years have passed since last update.

枠外をクリックして閉じれるドロップダウンの実装

Last updated at Posted at 2019-12-07

始めに

メニューなどでクリックしたらリストがドロップダウンで表示することがあると思いますが、その時関係のない場所をクリックしたら閉じるという機能をどう実装するかという話です。
(デザインが適当すぎるせいで伝わっているか不安・・・)
今回はVue.jsで実装する場合の説明をしたいと思います。

ドロップダウン.png

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までイベントが送られないようにしていました。ただこれすると他のドロップダウンをクリックしたときにそれ以外のドロップダウン要素が閉じてくれなかったりで色々難点がありました・・・。

12
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
14