1.目的

  看了很多element-ui的源码,决定自己实现一个简单的select组件,遇到的几个难点,便记录下来.

2.难点一

  element-ui中的select组件通过v-model可以绑定数据,但在我平时用v-model只是在input中使用过,只知道v-model可以双向绑定数据,但并不清楚其中的实现过程,所以 需要清晰的了解v-model是什么,如下.

<input v-model="test"/>  

<input :value="test" @input="test = $event.target.value"/> // 第一行和第二行的性质是一样的,v-model是一个vue的语法糖
 

  以上是input输入框中的v-model,input标签在输入的时候默认会触发'input'事件, 但是自定义的组件并不会,所以需要我们自己手动发送一个'input'事件,其次是,使用了v-model指令以后会默认动态绑定一个属性值value,因此我们在自定义组件中可以在props接收value,并绑定到组件当中,从而实现了双向绑定,具体可以看参考完整代码.

3.难点二

  当select组件显示选择框时,合理的逻辑是是点击空白或者点击自身都要将选择框关闭, 起初实现是在document中绑定一个click事件用于关闭选择框,当然select点击得阻止事件冒泡,这样的实现方式是在一个页面只有一个select组件是没有问题的,但是当出现多个select组件就会出现一个bug,点击完一个select以后点击另外一个是无法关闭前一个select框的选择框的,问题出在因为每个select框都被阻止了事件的冒泡,自然不会触发document的click事件,从而无法关闭,知晓原因,解决方案如下:

// 显示选择框
showSel(){
this.show = true;
addEvent(document, 'click',this.hideSel, true);
} // 隐藏选择框
hideSel(e){
this.show = false;
  // 如果是子元素,则阻止事件捕获
if(this.$refs.sel && this.$refs.sel.contains(e.target)){ stopEvent(e);
}
removeEvent(document,'click',this.hideSel,true);
} // 显示或隐藏
toggle(){
this.show && this.hideSel() || this.showSel();
} // 注意:其中addEvent,removeEvent,stopEvent是为了兼容处理而自定义的方法

  以上就是这次编写select组件的所得,附上完整实例代码.

<template>
<div class="select" @click="toggle" ref="sel">
<div class="input">
<input
type="text"
:placeholder="placeholder"
readonly
:value = 'value'
@blur="handle">
<img src="../images/drop.svg">
</div>
<ul
class="content"
:class="{'bottom' : position == 'bottom', 'top' : position == 'top'}"
v-show="show && values.length"
ref="content"> <li v-for="item in values">{{item}}</li>
</ul> </div>
</template> <script>
import { addEvent, removeEvent, stopEvent } from '../service/utli.js';
export default { name : 'comSelect',
data(){
return{
val : '',
show : false,
position : 'bottom'
}
},
props : {
values : {
type : Array,
default(){
return []
}
},
value : { },
placeholder:{
type : String,
default : '请选择'
},
},
mounted(){
this.computePos();
},
methods:{
getElementTop(element){
var actualTop = element.offsetTop;
     var current = element.offsetParent;
     while (current !== null){
actualTop += current.offsetTop;
current = current.offsetParent;
     }
     return actualTop;
}, // 计算选择框是往上弹出还是往下弹出
computePos(){ let elHeight = this.$refs.sel.offsetHeight;
let absPos = this.getElementTop(this.$refs.sel);
let contentHeight = this.values.length*40; let docScrollHei = document.body.scrollTop
|| document.documentElement.scrollTop || 0; let docHeight = document.documentElement.clientHeight
|| document.body.clientHeight || 0; if((elHeight+absPos+contentHeight-docScrollHei)>docHeight){
this.position = 'top';
}else{
this.position = 'bottom';
}
},
setVal(item){
this.$emit('input',item);
},
handle(){
this.$emit('blur');
},
showSel(){
this.show = true;
addEvent(document, 'click',this.hideSel, true);
},
hideSel(e){
this.show = false;
console.log(this.$refs.sel.contains(e.target));
if(this.$refs.sel && this.$refs.sel.contains(e.target)){
// 如果是子元素则阻止事件捕获
stopEvent(e);
          this.setVal(e.target.innerHtml);
}
removeEvent(document,'click',this.hideSel,true);
},
toggle(){
this.show && this.hideSel() || this.showSel();
}
}
}
</script> <style scoped lang="scss">
@import '../style/mixin.scss'; .select{
width: 100%;
height: 100%;
position: relative;
cursor: pointer;
}
.input{
width: 100%;
height: 100%;
position: relative;
cursor: pointer; }
.input>input{
width: 100%;
height: 100%;
cursor: pointer; }
.input>img{
right: 0;
top: 50%;
width: 12px;
height: 12px;
position: absolute;
transform: translateY(-50%);
} .content{
width: 100%;
max-height: px(300);
overflow-y: scroll;
border-radius: 10px;
@include padding(4px 0);
position: absolute;
left: 0;
background-color: white;
box-shadow: 0 0 20px 2px #ccc;
@include prix(transform, translateY(5px));
z-index: 2;
}
.content::-webkit-scrollbar {display: none;} .bottom{
top: 100%;
}
.top{
bottom: 125%;
}
.content>li{
height: 40px;
line-height: 40px;
width: 100%;
@include padding(0 0 0 10px);
} .content>li:hover{
color: #409eff;
background-color: rgba(33,33,33,.2);
} </style>

vue自定义select组件的更多相关文章

  1. vue 自定义分页组件

    vue2.5自定义分页组件,可设置每页显示条数,带跳转框直接跳转到相应页面 Pagination.vue 效果如下图: all: small(只显示数字和上一页和下一页): html <temp ...

  2. vue自定义分页组件---切图网

    vue2.5自定义分页组件 Pagination.vue,可设置每页显示条数,带跳转框直接跳转到相应页面,亲测有用.目前很多框架自带有分页组件比如elementUI,不过在面对一个拿到PSD稿,然后重 ...

  3. vue自定义全局组件(自定义插件)

    有时候我们在做开发的时候,就想自己写一个插件然后就可以使用自己的插件,那种成就感很强.博主最近研究element-ui和axios的时候,发现他们是自定义组件,但是唯一有一点不同的是,在用elemen ...

  4. vue 自定义报警组件

    1.自定义报警组件 Alarm.vue <!-- 报警 组件 --> <template> <div class="alarm"> <!- ...

  5. Vue自定义日历组件

    今天给大家介绍Vue的日历组件,可自定义样式.日历类型及支持扩展,可自定义事件回调.Props数据传输. 线上demo效果 示例 Template: <Calendar :sundayStart ...

  6. vue 自定义封装组件 使用 model 选项

    自定义组件的 v-model 一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框.复选框等类型的输入控件可能会将 value 特性用于不同 ...

  7. vue自定义日期组件

    vue-datepicker 基于 vuejs 2.x 可自定义主题的日期组件 github Usage 只需要在项目中加入 calendar.vue,就可以使用了. 向组件传入 prop 即可改变 ...

  8. vue 自定义image组件

    介绍 1:当图片加载失败时,给出错误提示. 2:当图片加载中时,给出加载提示. 3:图片处理模式:等比缩放/裁剪/填充/... 1.图片加载状态处理 通过给图片绑定load事件与error事件处理函数 ...

  9. Vue 自定义事件 && 组件通信

    1 App.vue 2 <template> 3 <!-- 4 组件的自定义事件: 5 1.一种组件间通信的方式,使用于:子组件===>父组件 6 2.使用场景:A是父组件,B ...

随机推荐

  1. JavaScript 原型 原型链

    一. 普通对象与函数对象 JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object .Function 是 JS 自带的函数对象.下面举例说明 var o1 = ...

  2. 洛谷P1914 小书童——密码

    题目背景 某蒟蒻迷上了"小书童",有一天登陆时忘记密码了(他没绑定邮箱or手机),于是便把问题抛给了神犇你. 题目描述 蒟蒻虽然忘记密码,但他还记得密码是由一串字母组成.且密码是由 ...

  3. [luogu3237 HNOI2014] 米特运输 (树形dp)

    传送门 Description 米特是D星球上一种非常神秘的物质,蕴含着巨大的能量.在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题. D星上有N个城市,我们将其顺序编号为1到N, ...

  4. Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存

    Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存 参考:<linux编程从入门到精通>,<Linux C程序设计大全>,<unix环境高级编程> ...

  5. Tensorflow高效读取数据的方法

    最新上传的mcnn中有完整的数据读写示例,可以参考. 关于Tensorflow读取数据,官网给出了三种方法: 供给数据(Feeding): 在TensorFlow程序运行的每一步, 让Python代码 ...

  6. BA-设计施工调试流程

    工程范围 1.楼宇自控系统的工程设计首先要了解目标建筑物所处的地理环境.建筑物用途.楼宇自控系统的建设目标定位.建筑设备规模与控制工艺及监控范围等工程情况.这些情况一般在工程招标技术文件中介绍,设计者 ...

  7. Ajax-URL 防止数据缓存,添加时间戳

    url:CONTEXTPATH + "/dataService/getSourceStatics?type=0&t="+new Date().getTime(),

  8. 一种基于Qt的可伸缩的全异步C/S架构server实现(五) 单层无中心集群

    五.单层无中心集群 对40万用户规模以内的server.使用星形的无中心连接是较为简便的实现方式.分布在各个物理server上的服务进程共同工作.每一个进程承担若干连接.为了实现这个功能,须要解决几个 ...

  9. 树莓派 rtl8188eu 芯片wifi驱动

    总算是找到了.现拿出来分享.參考地址:https://www.raspberrypi.org/forums/viewtopic.php? p=462982#p462982 下载的地址是:https:/ ...

  10. hdu5319 Painter(模拟)

    题目链接:点击打开链接 题目大意:给一个矩形.有两把刷子,一把刷红色,一把刷蓝色,红色的方向是东南,蓝色的方向是西北,红色加蓝色等于绿色,如今已知这面墙当前的状态.求从白墙到这个状态最少刷了多少次. ...