ps:只是说明思路,生产中不采用此方式

html代码:

<div id='app'>    
    <g-popover>        
        <template slot="content">            
            <div>popover里面的内容</div>        
        </template>        
    <button>点击</button>    
</g-popover></div>复制代码

vue组件代码:

<template>    
    <div class="popover" @click="onClick">        
        <div class="content-wrapper" v-if="visible">            
            <slot name="content"></slot>        
        </div>        
        <slot></slot>    
    </div>
</template>
<script>    
    export default {        
        name: "GuLuPopover",        
        data(){            
            return {visible:false}        
        },        
    methods:{            
        onClick(){                
        this.visible = !this.visible            
        }        
    }    
}</script>复制代码

通过给按钮添加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里面的内容怎么办?

上面两个问题都可以通过阻止冒泡解决

<div class="popover" @click.stop="onClick">
<div class="content-wrapper" v-if="visible" @click.stop>
<slot name="content"></slot>
</div>
<slot></slot>
</div>

click.stop解决bug的同时也会带来新的问题,就是用户无法给button添加click事件,所以这不是最终解决办法,同时popover不应该和button放在同一层级,否则用户写一个overflow:hidden,

popover就无法正确出现了





转载于:https://juejin.im/post/5cff1aa051882523e37696cb

Logo

前往低代码交流专区

更多推荐