vue中点击空白处隐藏div的实现(用指令优雅的实现)

159 阅读3分钟

简单想应该怎么实现? 1、肯定是给document增加一个click事件监听 2、当发生click事件的时候判断是否点击的当前对象 结合着本思路和指令咱们来实现。(文章最后面有效果图)


简单介绍vue指令

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

接下来我们来看一下钩子函数的参数 (即 el、binding、vnode 和 oldVnode)。

这里写图片描述

代码实现

创建指令对象,分析放在代码中

复制代码
<template>
	<div>
		<div class="show" v-show="show" v-clickoutside="handleClose">
			显示
		</div>
	</div>
</template>

<script>
const clickoutside = {
	// 初始化指令
    bind(el, binding, vnode) {
        function documentHandler(e) {
			// 这里判断点击的元素是否是本身,是本身,则返回
            if (el.contains(e.target)) {
                return false;
			}
			// 判断指令中是否绑定了函数
            if (binding.expression) {
				// 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
                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: 'HelloWorld',
    data() {
        return {
            show: true,
        };
    },
    directives: {clickoutside},
    methods: {
        handleClose(e) {
            this.show = false;
        },
    },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.show {
    width: 100px;
    height: 100px;
    background-color: red;
}
</style>

这个方法目前发现是有问题的, 实现不了点击一个按钮弹出一个弹框,点击弹框外的位置关闭弹框的需求,

究其原因,就是点击按钮的时候就相当于点击了弹框外的地方。

解决办法:阻止冒泡

点击空白处隐藏弹出层的原理是在 document 上绑定事件来隐藏弹出层,这样点击任何元素的时候都会冒泡到 document 上,都会执行隐藏弹出层的功能,所以我们只要在不需要隐藏弹出层的元素上阻止冒泡即可。

<div class="header-icon" @click.stop="editNotice">
      <i class="iconfont icon-bianji1"></i>
</div>

<div class="content" v-clickoutside="handleCloseNoticeEdit">
              <div :contenteditable="isNoticeEdit" :class="{ editborder: isNoticeEdit }" class="outEditableDiv">
                <p>1、颜色区分:可点击或者可选择部分鼠标放上去需要变色,已做颜色区分的按钮除外。</p>
                <p>2、工作项编号全系统唯一,编号为:(项目标识+数字(1,2,3逐渐增加))。</p>
                <p>3、工作项状态有:阶段(打开、进行中、已完成),里程碑(未完成、已完成),需求(打开、进行中、已完成、关闭),任务(打开、进行中、已完成、关闭)。</p>
                <!-- <p>4、一个父计划可对应多个子计划或者对应多个工作项,一个计划只能有一个父计划,一个工作项只能有一个父工作项。</p>
                <p>5、依赖关系存在于需求和任务,需求和需求,任务和任务之间的关系。</p> -->
              </div>
</div>

效果图:

动画.gif