[Vue.js2.x]可从外部关闭的下拉菜单的自定义指令

5,684 阅读2分钟

网页中有很多常见的下拉弹窗,比如下图所示的用户信息弹窗:

点击用户头像和名称,会弹出一个下拉菜单,然后点击页面中其它空白区域(除了弹窗本身外),弹窗就关闭了。本示例就用自定义指令来实现这样的需求。

先来分析一下如何实现。

该示例有两个特点,一是点击下拉菜单本身是不会关闭的,二是点击下拉菜单以外的所有区域都要关闭。点击所有区域,可以在 document 上绑定 click 事件来实现,同时只要过滤出是否点击的是目标元素内部的元素即可。

代码如下,只适用于 Vue.js 2.x

  • index.html:
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <title>可从外部关闭的下拉菜单</title>
      <link rel="stylesheet" type="text/css" href="style.css">
    </head>
    <body>
      <div id="app" v-cloak>
          <div class="main" v-clickoutside="handleClose">
              <button @click="show = !show">点击显示下拉菜单</button>
              <div class="dropdown" v-show="show">
                  <p>下拉框的内容,点击外面区域可以关闭</p>
              </div>
          </div>
      </div>
      <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
      <script src="clickoutside.js"></script>
      <script src="index.js"></script>
    </body>
    </html>
  • index.js:

    var app = new Vue({
      el: '#app',
      data: {
          show: false
      },
      methods: {
          handleClose: function () {
              this.show = false;
          }
      }
    });
  • clickoutside.js:

    Vue.directive('clickoutside', {
      bind (el, binding, vnode) {
          function documentHandler (e) {
              if (el.contains(e.target)) {
                  return false;
              }
              if (binding.expression) {
                  binding.value(e);
              }
          }
          el.__vueClickOutside__ = documentHandler;
          document.addEventListener('click', documentHandler);
      },
      unbind (el, binding) {
          document.removeEventListener('click', el.__vueClickOutside__);
          delete el.__vueClickOutside__;
      }
    });
  • style.css:
    [v-cloak] {
      display: none;
    }
    .main{
      width: 125px;
    }
    button{
      display: block;
      width: 100%;
      color: #fff;
      background-color: #39f;
      border: 0;
      padding: 6px;
      text-align: center;
      font-size: 12px;
      border-radius: 4px;
      cursor: pointer;
      outline: none;
      position: relative;
    }
    button:active{
      top: 1px;
      left: 1px;
    }
    .dropdown{
      width: 100%;
      height: 150px;
      margin: 5px 0;
      font-size: 12px;
      background-color: #fff;
      border-radius: 4px;
      box-shadow: 0 1px 6px rgba(0,0,0,.2);
    }
    .dropdown p{
      display: inline-block;
      padding: 6px;
    }