ps:只是说明思路,生产中不采用此方式
html代码:
<div id='app'>
<g-popover>
<template slot="content">
<div>popover里面的内容div>
template>
<button>点击button>
g-popover>div>复制代码
vue组件代码:
复制代码
通过给按钮添加click方法,切换visible的值来控制文本的显示与隐藏
接下来实现点击空白部分关闭popover,
最容易想到的思路是在切换visible时监听document的click事件
// 建议监听document而不是document.body,否则关闭区域会受body高度影响
onClick(){ this.visible = !this.visible if(this.visible === true){ document.addEventListener('click',()=>{ this.visible = false }) }复制代码
这时会发现popover没有出现,我们来看log
onClick(){ this.visible = !this.visible console.log('改变visible的值') if(this.visible === true){ document.addEventListener('click',()=>{ console.log('点击document隐藏popover') this.visible = false }) }复制代码
浏览器控制台
两个log依次执行,所以popover刚开启就被关闭了
改用异步代码
if (this.visible === true) { setTimeout(() => { document.addEventListener("click", () => { console.log("点击document隐藏popover"); this.visible = false; }); }, 500); }复制代码
多点击几次,会发现popover组件出问题了,点击一次会执行很多次log
分析代码会发现,我们没有清除click事件队列,导致事件越来越多
在popover隐藏后要清除click事件,为了方便,改写剪头函数,并绑定this
if (this.visible === true) { setTimeout(() => { document.addEventListener("click", function x(){ console.log("点击document隐藏popover"); this.visible = false; document.removeEventListener('click',x) }.bind(this)); }, 500); }复制代码
结果发现,事件监听队列还是继续叠加,好像并没有成功的删除监听器
这是因为x.bind会生成一个新的函数,所以我们添加的函数和我们的删除的函数并不是同一个
解决方法是给箭头函数取一个名字
if (this.visible === true) { setTimeout(() => { let eventHandler = ()=>{ this.visible = false; console.log('点击document关闭popover') document.removeEventListener('click',eventHandler) } document.addEventListener("click", eventHandler); }); }复制代码
浏览器控制台
好了,暂时解决了一个bug
另一个细节是popover出现后,点击button按钮会隐藏两次popover
第一次是button按钮点击后,切换visible的值,组件自身隐藏popover
同时button按钮也在document区域内,点击按钮同样会触发事件队列,再隐藏一次popover
另一个bug是点击popover区域也会隐藏popover
这明显是不合理的,如果用户要复制popover里面的内容怎么办?
上面两个问题都可以通过阻止冒泡解决
click.stop解决bug的同时也会带来新的问题,就是用户无法给button添加click事件,所以这不是最终解决办法,同时popover不应该和button放在同一层级,否则用户写一个overflow:hidden,
popover就无法正确出现了