vue实现一个简易Popover组件
概述
之前写vue的时候,对于下拉框,我是通过在组件内设置标记来控制是否弹出的,但是这样有一个问题,就是点击组件外部的时候,怎么也控制不了下拉框的关闭,用户体验非常差。
当时想到的解决方法是:给根实例创建一个标记来控制,然后一级一级的把这个标记传进来。但是这样每次配置都要改根组件,非常不灵活。
最近看museUI库,发现它的下拉框Select实现的非常灵活,点击组件外也能控制下拉框关闭,于是想探究一番,借此机会也深入学习一下vue。
museUI源码
首先去看Select的源码:
directives: [{
name: 'click-outside',
value: (e) => {
if (this.open && this.$refs.popover.$el.contains(e.target)) return;
this.blur();
}
}],
可以看到,有个click-outside和popover,然后它是通过用自定义指令directives实现的。然后去museUI搜popover,果然这是一个弹出组件,并且能够在组件外部控制弹窗关闭。于是开始看popover的源码:
close (reason) {
if (!this.open) return;
this.$emit('update:open', false);
this.$emit('close', reason);
},
clickOutSide (e) {
if (this.trigger && this.trigger.contains(e.target)) return;
this.close('clickOutSide');
},
可以看到,它也是通过click-outside来实现的,click-outside字面意思是点击外面,应该就是这个了。然后看click-outside的源码:
name: 'click-outside',
bind (el, binding, vnode) {
const documentHandler = function (e) {
if (!vnode.context || el.contains(e.target)) return;
if (binding.expression) {
vnode.context[el[clickoutsideContext].methodName](e);
} else {
el[clickoutsideContext].bindingFn(e);
}
};
el[clickoutsideContext] = {
documentHandler,
methodName: binding.expression,
bindingFn: binding.value
};
setTimeout(() => {
document.addEventListener('click', documentHandler);
}, 0);
},
原来它是通过自定义指令,在组件创建的时候,给document绑定一个全局click事件,当点击document的时候,通过判断点击节点来控制弹窗关闭的。这差不多就是事件代理。
所以总结一下,要实现组件外部控制组件弹窗的关闭,主要利用directives,bind,document就行了。
自己实现
既然知道原理就有点跃跃欲试了,通过查阅官方文档得知,directives可以用于局部组件,这样就变成了局部指令。于是写代码如下:
<template>
<div class="pop-over">
<a @click="toggleOpen" class="pop-button" href="javascript: void(0);">
{{ 按钮1 }}
</a>
<ul v-clickoutside="close" v-show="open" class="pop-list">
<li>选项1</li>
<li>选项2</li>
<li>选项3</li>
<li>选项4</li>
</ul>
</div>
</template>
<script>
export default {
name: 'PopOver',
data() {
return {
open: false
}
},
methods: {
toggleOpen: function() {
this.open = !this.open;
},
close: function(e) {
if(this.$el.contains(e.target)) return;
this.open = false;
}
},
directives: {
clickoutside: {
bind: function (el, binding, vnode) {
const documentHandler = function (e) {
if (!vnode.context || el.contains(e.target)) return;
binding.value(e);
};
setTimeout(() => {
document.addEventListener('click', documentHandler);
}, 0);
}
}
}
}
</script>
注意,在我们close方法里面,我们通过判断点击节点是否被组件包含,如果包含的话,不执行关闭行为。
但是上面的组件不通用,正好官方文档学习了slot,于是用slot改写如下:
<template>
<div class="pop-over">
<a @click="toggleOpen" class="pop-button" href="javascript: void(0);">
{{ buttonText }}
</a>
<ul v-clickoutside="close" v-show="open" class="pop-list">
<slot></slot>
</ul>
</div>
</template>
<script>
export default {
name: 'PopOver',
props: ['buttonText'],
data() {
return {
open: false
}
},
methods: {
toggleOpen: function() {
this.open = !this.open;
},
close: function(e) {
if(this.$el.contains(e.target)) return;
this.open = false;
}
},
directives: {
clickoutside: {
bind: function (el, binding, vnode) {
const documentHandler = function (e) {
if (!vnode.context || el.contains(e.target)) return;
binding.value(e);
};
setTimeout(() => {
document.addEventListener('click', documentHandler);
}, 0);
}
}
}
}
</script>
<style scoped>
.pop-over {
position: relative;
width: 100%;
height: 100%;
}
.pop-button {
position: relative;
width: 100%;
height: 100%;
text-decoration:none;
color: inherit;
}
.pop-list {
position: absolute;
left: 0;
top: 0;
}
.pop-list li {
width: 100%;
height: 100%;
padding: 8px 3px;
list-style:none;
}
</style>
利用props自定义按钮文字,slot自定义弹窗文字,这样一个简易的Popover组件就完成了。
我学到了什么
- 对directives自定义指定,事件代理,slot练手一番,感觉很爽。
- 在看源码的过程中,也看到了render方法的使用,以及museUI的组件化思想。
- 对于组件外控制组件的行为有了新的思路。
vue实现一个简易Popover组件的更多相关文章
- 过年了,基于Vue做一个消息通知组件
前言 今天除夕,在这里祝大家新年快乐!!!今天在这个特别的日子里我们做一个消息通知组件,好,我们开始行动起来吧!!!项目一览 效果很简单,就是这种的小卡片似的效果. 我们先开始写UI页面,可自定义消息 ...
- vue封装一个弹框组件
这是一个提示框和对话框,例: 这是一个组件 eject.vue <template> <div class='kz-cont' v-show='showstate'> &l ...
- Angular2-编写一个简易的组件
Angular2组件可以这么理解:编写一个类,然后在类的上面用组件装饰器装饰一下,这个类就成组件了. 所以编写组件分两步:1)编写类:2)编写装饰器 1)编写类: export class Simpl ...
- vue实现一个会员卡的组件(可以动态传入图片(分出的一个组件)、背景、文字、卡号等)
自己在写这个组件的时候主要遇到的问题就是在动态传入背景图片或者背景色的时候没能立马顺利写出来,不过现在实现了这个简单组件就和大家分享一下 <template> <div class= ...
- vue小案例--简易评论区
一.小案例(评论区) 1.流程 (1)分析静态页面.(vue项目创建参考https://www.cnblogs.com/l-y-h/p/11241503.html)(2)拆分静态页面,变成一个个组件. ...
- vue + socket.io实现一个简易聊天室
vue + vuex + elementUi + socket.io实现一个简易的在线聊天室,提高自己在对vue系列在项目中应用的深度.因为学会一个库或者框架容易,但要结合项目使用一个库或框架就不是那 ...
- 发布自己第一个npm 组件包(基于Vue的文字跑马灯组件)
一.前言 总结下最近工作上在移动端实现的一个跑马灯效果,最终效果如下: 印象中好像HTML标签的'marquee'的直接可以实现这个效果,不过 HTML标准中已经废弃了'marquee'标签 既然HT ...
- 一个vue的全局提示框组件
<template> <!-- 全局提示框 --> <div v-show="visible" class="dialog-tips dia ...
- Vue 实现一个分页组件
实现分页组件要分三个部分 样式,逻辑,和引用 首先新建一个vue文件用来承载组件内容 第一步:构建样式 <template> <nav> <ul class=" ...
随机推荐
- import os, glob, fnmatch--Python os/glob/fnmatch主要函数总结
auther: Lart date: 2019-01-17 update: 2019-01-18 09:55:36 --- import os, glob, fnmatch 针对某些操作, 官方推荐这 ...
- Angular实现动态添加删除表单输入框功能
<div class="form-group form-group-sm" *ngFor="let i of login"> <label c ...
- github node.js
#安装githubyum install git -y #下载nvmgit clone git://github.com/creationix/nvm.git ~/nvm #设置nvm 自动运行;ec ...
- 01-SpringMVC 原理
说明:所有代码调式的环境:开发环境idea,jdk7,tomcat8.5.27,数据库MySQL5.1,spring3.2 SpringMVC 1.什么是SpringMVC? springmvc是sp ...
- java学习(四)
学号 20189214 <Java程序设计>第四周学习总结 教材学习内容总结 枚举 枚举是JDK1.5版本新增的特性(泛型.For-each等如今被广泛应用的特性也是由JDK1.5时所新增 ...
- [Battle]Battle章1 Presto VS Impala
Round One: 平局:共同点就是吃内存; Round Two: Impala胜 Impala查询性能稍领先于presto Round Three: presto胜 presto在数据源支持上非常 ...
- SQLPrompt 安装后sql上看不到菜单
解决方案很简单,请使用管理员权限安装 破解也是一样,需要管理员权限运行
- WCF系列_WCF如何选择不同的绑定
内容转载自<WCF核心技术> 开发者不用直接操作信道范型,而是由WCF根据服务OperationContract来选择合适的信道范型.大多数信道范型都有无会话两种变体.有会话信道会在客户端 ...
- ASP.NET MVC4添加区域视图 找到多个与名为“home”的控制器匹配的类型
今天在项目中遇到一个问题,在MVC下想建立一个区域的后台Boss视图,出现了"找到多个与名为“home”的控制器匹配的类型"的问题,希望下面的解决方案能够帮助到大家 这是网站的整体 ...
- 软件测试---测试模型(V、W、H)
一.V测试模型 1.V模型示意图: 单元测试:又叫模块测试,针对软件设计中的最小单位—>程序模块 集成测试:又叫组装测试,通常在单元测试的基础上,将所有程序模块进行有序.递增测试. 系统测试:把 ...