vue实现带搜索的模拟下拉框组件先上效果图:
组件特点:
模拟下拉框
可输⼊⽂字搜索选项,keyup或input事件触发搜索(并优化了原⽣keyup和input事件的问题)
数据源异步加载
滚动加载选项数据
同⼀页⾯可重复使⽤该组件
技术⼯具说明:
基础框架 vue.js
jquery.js辅助
样式 element-ui.js
注意:⾮单页⾯,⾮前后端分离开发
进⼊正⽂:
创建项⽬
1. 新建项⽬⽂件夹 cw-input-select
打开,以下内容都是在此⽂件夹内操作,不赘述
2. 新建demo.html
1. 引⽤jquery,vue,element-ui(cdn)
2. 创建⼀个id="app"的根元素
3. 在body元素尾部添加<script></script>标签,创建vue实例(这部分代码也可以单独写⼀个js⽂件)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="cdn.bootcss/jquery/3.4.1/jquery.min.js"></script>
<script src="cdn.bootcss/vue/2.6.10/vue.min.js"></script>
<script src="cdn.bootcss/element-ui/2.10.1/index.js"></script>
</head>
<body>
<div id="app">
</div>
<script>
new Vue({
el: '#app'
})
</script>
</body>
</html>
3. 新建⽂件夹component,并在⾥⾯新建cw-input-select.js和cw-input-select.css⽂件
结构如下:
| cw-input-select
| demo.html
| component
| cw-input-select.js
| cw-input-select.css
4. 在demo.html header标签⾥引⼊cw-input-select.js和cw-input-select.css
<!-- 引⼊cw-input-select组件 -->
<link rel="stylesheet" href="component/cw-input-select.css">
<script src="component/cw-input-select.js"></script>
编写组件
1.编写⼀个vue全局组件 cw-input-select
component/cw-input-select.js
Vueponent('cw-input-select', {
template: '<div></div>',
data: function () {
return {}
},
created () {},
methods: {},
})
2.在demo.html 添加组件标签
demo.html
<div id="app">
<cw-input-select></cw-input-select>
</div>
3. 实现静态(⽆动态数据⽆交互)的下拉框布局和样式,并⽤静态数组模拟选项(后⾯替换动态数据源)component/cw-input-select.js
Vueponent('cw-input-select', {
template: `<div class="cw-input-select_wrap">
<div class="cw-input-select">
<div class="cw-input-select_box">
jquery是什么功能组件<span>请选择</span>
<i class="cw-arrow"></i>
</div>
<div class="cw-input-select_pop">
<input type="text" class="cw-input-select_ipt" placeholder="搜索" />
<ul class="cw-input-select_options">
<li>
<span>不限</span>
</li>
<li v-for="option in optionsList">
{{option}}
</li>
</ul>
<span class="cw-input-select_arrow"></span>
</div>
</div>
</div>`,
data: function () {
return {
optionsList: ['选项1', '选项2', '选项3']
}
},
created () {},
methods: {},
})
component/cw-input-select.css
/* 基础样式reset */
input {
box-sizing: border-box;
outline: 0;
}
ul {
margin: 0;
padding: 0;
}
ul, li {
list-style: none;
}
/* 组件整体容器 */
.
cw-input-select_wrap {
position: relative;
width: 198px;
height: 28px;
font-size: 14px;
}
/* 组件内容 */
.cw-input-select {
width: 198px;
position: absolute;
}
/
* 基本下拉框 */
.cw-input-select_box {
height: 28px;
border: 1px solid #b7b7b7;
border-radius: 4px;
background-color: white;
position: relative;
cursor: pointer;
}
/* 基本下拉框⾥⾯右边的线体上下箭头(可旋转) */
.cw-arrow {
content: '';
display: block;
position: absolute;
right: 10px;
top: 8px;
border-top: 1px solid #C0C4CC;
border-right: 1px solid #C0C4CC;
border-radius: 1px;
width: 8px;
height: 8px;
background: transparent;
transition: transform .3s, -webkit-transform .3s;
transform: rotate(135deg);
z-index: 10;
}
/* 基本下拉框⽂字 */
.cw-input-select_box > span {
display: inline-block;
line-height: 28px;
padding: 0 30px 0 15px;
font-size: 12px;
color: #606266;
/
* ⽂字超出⽤省略号 */
white-space: nowrap;
text-overflow: ellipsis;
width: 100%;
overflow: hidden;
}
/* 选项列表盒⼦ */
.cw-input-select_pop {
position: relative;
background-color: white;
border: 1px solid #E4E7ED;
border-radius: 4px;
max-height: 274px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
margin-top: 12px;
padding: 5px;
box-sizing: border-box;
z-index: 9;
}
/* 选项列表盒⼦上⽅的三⾓形箭头 */
.cw-input-select_arrow {
position: absolute;
display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, .03));
top: -6px;
left: 35px;
margin-right: 3px;
border-top-width: 0;
border-bottom-color: #fff;
z-index: 99;
}
/* 选项列表盒⼦⾥⾯的输⼊框 */
.cw-input-select .cw-input-select_pop .cw-input-select_ipt { position: absolute;
top: 5px;
z-index: 99;
height: 24px;
line-height: 20px;
width: 94%;
border: 1px solid #DCDFE6;
padding: 1px 5px;
font-size: 12px;
}
/* 选项列表内容 */
.cw-input-select_options {
display: block;
margin-top: 26px;
max-height: 234px;
}
/* 选项单元 */
.cw-input-select_options li {
padding: 8px 15px;
background-color: white;
cursor: pointer;
}
/* 选项单元hover */
.cw-input-select_options li:hover {
background-color: #F5F7FA;
}
点击查看css
css注释我尽量写详细,因为很难把css分开讲解。⾄此,效果如下:
-
4. 实现点击基本框显⽰或隐藏选项列表盒⼦,线体箭头可上下旋转
(1)在基本框class=cw-input-select_box的div上添加click事件调⽤selectHandle⽅法
<div class ="cw-input-select_box" v-on:click="selectHandle">
(2)在组件实例data选项⾥添加⼀个变量isShowPop,methods选项⾥添加selectHandle⽅法
data: function () {
return {
optionsList: ['选项1', '选项2', '选项3'],
isShowPop: false
}
},
methods: {
/
/ 点击基本框显⽰或隐藏选项列表盒⼦
selectHandle: function () {
this.isShowPop = !this.isShowPop;
},
},
(3)在class=cw-input-select_pop的div上添加v-if="isShowPop"
<div class="cw-input-select_pop" v-if="isShowPop">
(4)为了测试多个组件是否互相⼲扰,可在demo.html添加多个<cw-input-select></cw-input-select>看看效果。不会⼲扰。
(5)此时有个⼩问题
每次点击组件,很容易把⽂字选中,我们使⽤下拉框并不需要⽂字选中效果。
解决办法:
在组件最外层div加上 onselectstart="return false"
<div class="cw-input-select_wrap" onselectstart="return false">
就好了。现在⽆论点击多少次⽂字都不会被选中。
(6)基本框右侧箭头的旋转
这个只需要css就可以搞定
在cw-input-select.css⽂件的.cw-arrow下⾯添加.cw-arrow.up
/* 箭头向上 */
.cw-arrow.up {
transform: rotate(-45deg);
top: 12px;
}
回到组件js⽂件
<i class="cw-arror"></i> 添加 v-bind:class="{'up': isShowPop}"
<i class="cw-arrow" v-bind:class="{'up': isShowPop}"></i>
⾄此完成了点击基本框显⽰和隐藏选项列表的功能。
5. 点击本组件以外的范围,隐藏选项列表盒⼦(如果它正显⽰着)
怎么实现?在body上添加⼀个点击事件?组件内部是⽆法操纵调⽤它的⽗级页⾯的,也不是⽆法操纵,记得vue⽂档上说过,最好不要这么⼲。但现在没有别的办法,且先如此尝试⼀下。
(1)先在组件methods⾥定义⼀个专门⽤来隐藏的⽅法hidePop
hidePop: function () {
this.isShowPop = false;
}
(2)再在组件created钩⼦函数⾥⾯给body绑定click事件,调⽤hidePop⽅法,并排除组件⾃⾝的范围
created: function () {
// 点全局范围收起下拉框
var that = this;
$('body').click(function (e) {
console.log(e);
if (e.target.className=='cw-input-select_wrap' || $(e.target).parents('.cw-input-select_wrap').length>0) {
return;
}
that.hidePop();
});
},
这⾥注意,因为页⾯上内容少,body的⾼度也很⼩,所以点击页⾯空⽩处是不会触发body上的click事件的。此时,需要给body设置⾼度。⾄此demo.html完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="cdn.bootcss/jquery/3.4.1/jquery.min.js"></script>
<script src="cdn.bootcss/vue/2.6.10/vue.min.js"></script>
<script src="cdn.bootcss/element-ui/2.10.1/index.js"></script>
<!-- 引⼊cw-input-select组件 -->
<link rel="stylesheet" href="component/cw-input-select.css">
<script src="component/cw-input-select.js"></script>
<style>
body {
height: 100vh;
}
</style>
</head>
<body>
<div id="app">
<div >
<cw-input-select></cw-input-select>
</div>
<div >
<cw-input-select></cw-input-select>
</div>
</div>
<script>
new Vue({
el: '#app'
})
</script>
</body>
</html>
查看demo.html代码
这⼀需求基本完成,但是⼜有个⼩问题,点击另⼀个组件的时候,其他组件的选项列表盒⼦是不会隐藏的。因为所有的组件都满⾜ e.target.className=='cw-input-select_wrap',如何区别让当前点击的这个组件?
思来想去,我也没有更好的办法,最简单直接的办法就是在⽗页⾯调⽤组件的时候传⼊⼀个唯⼀识别——componentId,当然我试过在组件内部⽤13位时间戳⽣成唯⼀componentId,结果就是多个组件可能⽣成重复的componentId,这是为什么?说明vue组件⽣成的时间⽐时间戳还快。
(3)组件props选项,添加componentId接⼝
props: ['componentId'],
(4)在组件最外层容器上添加 v-bind:id="componentId"
<div v-bind:id="componentId" class="cw-input-select_wrap" v-bind:class="{'open': isShowPop}" onselectstart="return false">
(5)在demo.html 组件标签上添加 component-id属性传⼊不⼀样的值
<div id="app">
<div >
<cw-input-select component-id="cw-input-select-1"></cw-input-select>
</div>
<div >
<cw-input-select component-id="cw-input-select-2"></cw-input-select>
</div>
<div >
<cw-input-select component-id="cw-input-select-3"></cw-input-select>
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论