Vue折腾记 - (3)写一个不大靠谱的typeahead组件
2017-07-20 15:17
666 查看
前言
typeahead在网站中的应用很多..今天跟着我来写一个不大靠谱的
typeahead;
你能学到什么?
自定义事件
遍历的思想
功能细节的考虑
一切都挺不靠谱的…可完善的地方很多.废话不多说,看效果图
更新
2017-07-21: 完善逻辑及美化样式,所以效果图和代码都有所变动效果图
有哪些功能点?
粗糙的模糊搜索 - 借助
indexOf
ESC和
blur事件清除输入框,没有找到匹配的情况下
Enter默认在找到只剩下一个情况下选中
方向盘的上下(已经阻止光标的移动)选中子项,回车选中
鼠标点击选择子项
搜索框清空情况下默认不触发自定义事件值的返回
鼠标移动+键盘方向键移动位置的同步
placeholder及遍历数据
data支持外部传入,也就是绑定
props;前者字符串,后者数组对象
代码
typeahead.vue<template> <div class="typeahead"> <div class="typeahead-header"> <input type="text" v-model="searchVal" @input="filterList(searchVal)" ref="input" :placeholder="placeholder" @keydown.down.prevent="selectChildWidthArrowDown" @keydown.up.prevent="selectChildWidthArrowUp" @keydown.enter="selectChildWidthEnter" @blur="ifNotFoundClear" @keydown.esc="ifNotFoundClear" autocomplete="off"> </div> <transition name="el-fade-in-linear" mode="out-in"> <div class="typeahead-content" v-show="searchVal && searchVal.length"> <transition-group tag="ul" name="el-fade-in-linear" v-show="searchList && searchList.length > 0 && isExpand "> <li v-for="(item,index) in searchList" :key="index" :class="item.active ? 'active':''" @click="selectChild(index)" @mouseenter="setActiveClass(index)" @mouseleave="setActiveClass(index)"> <a href="javascript:;"> {{item.text}} </a> </li> </transition-group> <p class="noFound" v-show="searchList && searchList.length === 0">未能查询到,请重新输入!</p> </div> </transition> </div> </template> <script> export default { name: 'typeahead', data: function () { return { searchVal: '', // 搜索关键字 resultVal: '', // 保存搜索到的值 searchList: [], //保存过滤的结果集 currentIndex: -1, // 当前默认选中的index, isExpand: false } }, computed: { typeaheadData () { return this.mapData; } }, props: { placeholder: { type: String, default: '请输入您要查询的关键字' }, mapData: { type: Array, default: function () { return [ { text: 'wofsdf', value: 0 }, { text: '我是技术渣1', value: '1' }, { text: '我是技术渣2', value: '2' }, { text: '我是天坑', value: '2' }, { text: '我是天坑,分身乏术', value: '3' }, { text: '我是天坑2,分身乏术', value: '3' }, { text: '我是天坑3,分身乏术', value: '3' } ] } } }, methods: { filterList (searchVal) { // 过滤数组 if (this.searchVal === '') { this.ifNotFoundClear(); } else { this.searchList = []; // 清空列表 let tempArr = []; // 一个临时数组 this.currentIndex = -1; // 重置index this.typeaheadData && this.typeaheadData.forEach(item => { if (item.text.indexOf(searchVal.trim()) !== -1) { tempArr.push(item); } }); this.searchList = tempArr; // 为什么要一个临时数组,不然每次push都会触发视图更新..数据量一大.... this.isExpand = true; } }, ifNotFoundClear () { // 若是结果集长度为0就证明没有找到...赋值为空 if (this.searchList.length === 0) { this.searchVal = ''; this.isExpand = false; } this.searchList.forEach(item => { item.active = false; }) }, selectChild (index) { // 鼠标点击选择子项 this.searchList.forEach((item, innerIndex) => { if (index === innerIndex || item.active) { this.searchVal = item.text; this.resultVal = item.value; this.isExpand = false; } this.$set(item, 'active', false); }) this.$emit('selectValue', { text: this.searchVal, value: this.resultVal }); }, selectChildWidthArrowDown () { // 判断index选中子项 if (this.currentIndex < this.searchList.length) { this.currentIndex++; this.searchList.forEach((item, index) => { this.currentIndex === index ? this.$set(item, 'active', true) : this.$set(item, 'active', false); }) } }, selectChildWidthArrowUp () { // 判断index选中子项 if (this.currentIndex > 0) { this.currentIndex--; this.searchList.forEach((item, index) => { this.currentIndex === index ? this.$set(item, 'active', true) : this.$set(item, 'active', false); }) } }, selectChildWidthEnter () { // 若是结果集只有一个,则默认选中 if (this.searchList.length === 1) { this.searchVal = this.searchList[0].text; this.resultVal = this.searchList[0].value; this.isExpand = false; this.$emit('selectValue', { text: this.searchVal, value: this.resultVal }) } else { // 若是搜索的内容完全匹配到项内的内容,则默认选中 this.searchList.forEach(item => { if (this.searchVal === item.text || item.active === true) { this.searchVal = item.text; this.resultVal = item.value; this.isExpand = false; this.$emit('selectValue', { text: this.searchVal, value: this.resultVal }) } }) } }, setActiveClass (index) { this.searchList.forEach((item, innerIndex) => { if (index === innerIndex) { this.$set(item, 'active', true); this.currentIndex = index; // 这句话是用来修正index,,就是键盘上下键的索引,不然会跳位 } else { this.$set(item, 'active', false) } }) } } } </script> <style scoped lang="scss"> .el-fade-in-linear-enter-active, .el-fade-in-linear-leave-active, .fade-in-linear-enter-active, .fade-in-linear-leave-active { transition: opacity .2s linear; } .el-fade-in-enter, .el-fade-in-leave-active, .el-fade-in-linear-enter, .el-fade-in-linear-leave, .el-fade-in-linear-leave-active, .fade-in-linear-enter, .fade-in-linear-leave, .fade-in-linear-leave-active { opacity: 0; } .typeahead { position: relative; background-color: #fff; a { color: #333; text-decoration: none; padding: 5px; } ul { list-style: none; padding: 6px 0; margin: 0; overflow: visible; li { display: block; width: 100%; padding: 5px; font-size: 14px; padding: 8px 10px; position: relative; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #48576a; height: 36px; line-height: 1.5; box-sizing: border-box; cursor: pointer; &.active { background-color: #20a0ff; a { color: #fff; } } } } input { -webkit-appearance: none; -moz-appearance: none; appearance: none; background-color: #fff; background-image: none; border-radius: 4px; border: 1px solid #bfcbd9; box-sizing: border-box; color: #1f2d3d; font-size: inherit; height: 36px; line-height: 1; outline: 0; padding: 3px 10px; transition: border-color .2s cubic-bezier(.645, .045, .355, 1); width: 100%; display: inline-block; } .typeahead-header, .typeahead-content { width: 100%; } .typeahead-content { position: absolute; border-radius: 2px; background-color: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04); box-sizing: border-box; margin: 5px 0; } .noFound { text-align: center; } } </style>
总结
自此,一个挺粗糙的模糊搜索组件就完成了;希望此文对于正在阅读的您有所收获~~
有更好的方案或者实现方法的可以留言…谢谢
相关文章推荐
- Vue 折腾记 - (9) 写一个挺靠谱的typeahead组件
- Vue 折腾记 - (4) 写一个不大靠谱的 loading 组件
- Vue折腾记 - (2)写一个不大靠谱的面包屑组件
- Vue 折腾记 - (6) 写一个不大靠谱的backToTop组件
- Vue 折腾记 - (5) 写一个不大靠谱的selectSearch组件
- Vue 折腾记 - (8) 写一个挺靠谱的多地区选择组件
- Vue实现typeahead组件功能(非常靠谱)
- Vue折腾记 - (1)写一个不大靠谱的二级侧边栏
- Vue 折腾记 - (7) 写一个挺不靠谱的Vue-Echarts组件
- Angular 2 + 折腾记 :(8) 动手写一个不怎么靠谱的上传组件
- 写一个移动端惯性滑动&回弹Vue导航栏组件 ly-tab
- vue项目中编写一个图片预览的公用组件
- VUE -- 如何快速的写出一个Vue的icon组件?
- VUE实现一个分页组件
- 利用vue-cropper做的关于图片裁剪、压缩、上传、预览等做的一个公共组件
- 第六章 使用 Bootstrap Typeahead 组件(百度下拉效果)(续)
- 一个简单的Vue.js组件开发示例
- 结合Vuex创造一个完美的vue-loading/vue-toast组件
- 详解Vue用自定义指令完成一个下拉菜单(select组件)
- VUE开发一个图片轮播的组件示例代码