点击空白关闭弹窗

点击空白处关闭弹窗是常见的网页效果,其实它的实现原理很简单,就是全局监听一个点击事件,当监听到用户点击时,判断target元素是否在弹窗内,如果是,则不关闭弹窗(点击了关闭按钮例外),否则关闭弹窗。但需要注意一点,当弹窗显示时,才需要监听用户的点击事件,弹窗关闭时,需要及时撤销监听函数。本文介绍两种实现方式,供大家参考学习。

常规方法

template

<button ref="toggleButton" @click="handleClick">show</button>
<div ref="popup" class="popup" v-show="visible">
  Popup
  <button @click="handleClick">close</button>
</div>

script

<script>
export default {
  data() {
    return {
      visible: false
    }
  },
  methods: {
    // 弹窗显示时监听点击事件
    show() {
      document.addEventListener('click', this.hidePanel, false)
    },
    // 弹窗隐藏时撤销监听事件
    hide() {
      document.removeEventListener('click', this.hidePanel, false)
    },
    // 只有当点击的元素属于弹窗或者 toggle 按钮才会执行 if 里面的代码
    hidePanel(e) {
      if (!this.$refs.toggleButton.contains(e.target) && !this.$refs.popup.contains(e.target)) {
        this.visible = false
        this.hide()
      }
    },
    handleClick() {
      this.visible = !this.visible
      if (this.visible) {
        this.show()
      } else {
        this.hide()
      }
    }
  }
}
</script>

自定义指令

template

<button ref="toggleButton" @click="visible = !visible">show</button>
<!-- 这里用 v-if 代替了 v-show  -->
<div ref="popup" class="popup" v-if="visible" v-clickoutside="close">
  Popup
  <button @click="close">close</button>
</div>

script

<script>
const clickoutside = {
  // 初始化指令
  bind(el, binding, vnode) {
    function documentHandler(e) {
      // 这里判断点击的元素是否是本身,是本身,则返回
      if (el.contains(e.target)) {
        return false
      }
      // 判断指令中是否绑定了函数
      if (binding.expression) {
        // 如果绑定了函数 则调用那个函数,此处binding.value就是 close 方法
        binding.value(e)
      }
    }
    // 给当前元素绑定个私有变量,方便在 unbind 中可以解除事件监听
    el.__vueClickOutside__ = documentHandler
    document.addEventListener('click', documentHandler)
  },
  update() {},
  unbind(el, binding) {
    // 解除事件监听
    document.removeEventListener('click', el.__vueClickOutside__)
    delete el.__vueClickOutside__
  }
}

export default {
  name: 'App',

  directives: { clickoutside },

  data() {
    return {
      visible: false
    }
  },
  methods: {
    close(e) {
      if (!this.$refs.toggleButton.contains(e.target)) {
        this.visible = false
      }
    }
  }
}
</script>

bind方法里添加点击事件监听器,而在unbind里撤销监听器。值得注意的是,unbind只有在元素与指令解绑时(比如元素销毁)才会调用一次,所以弹窗必须用v-if而不是v-show,因为v-show只会隐藏元素,不会真正销毁元素。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。