输入框输入字符后,联想词列表出现,能够按“↓”或“↑”选择列表中的内容,也能够鼠标点选,且互相不影响选择样式,即只会出现一个被选中,“Enter”键发起检索。javascript
<template> <div class="input-m"> <div class="search"> <input type="text" :placeholder=placeholder v-model="content" @keyup="input"> </div> <ul class="associate-list" v-show="associateWords&&showList" @mouseout="selectedLi(0)"> <li class="associate-item">{{inputContent}}</li> <li class="associate-item" v-for="(item,index) of associateWords" :key="index" @mouseover="selectedLi(index+1)">{{item}}</li> </ul> </div> </template>复制代码
inputConent是用于记录用户经过键盘输入的内容,经过上下键选择或鼠标悬浮时选择的会经过双向绑定同步到content中,以百度搜索联想词列表为例,当用户一直按向下键时,超过联想词列表后,input框中的内容为用户最开始输入的内容。html
focusIndex记录用户选择的<li>标签的下标,当一直按向上或向下键时,会出现focusIndex超出<li>列表长度的或小于0的清理,用focusIndex = (focusIndex+length)%length操做,能够实现fousIndex老是在0至length-1范围内。java
当经过document.getElementsByClassName获取<li>数组时,数组下标从0开始,而foucusIndex初始值为0,当按下“↓”时,focusIndex+1,选中的就是<li>列表的下标为1的元素,即第2个<li>,这是不合理的。node
若是将focusIndex的下标初始值设为-1,知足了上边的需求。那么当按下“↑”时,focusIndex-1,经过取余操做能够获得foucusIndex = length-2,即<li>列表的倒数第2项,也是不合理的。ios
故将用户输入的文字内容做为<li>列表的第一项,且隐藏,将focusIndex初始值设为0。这样就实现了按上下键选择,且超出显示的长度时,是用户经过键盘输入的内容。json
用showList与associateWords一块儿控制列表的显示,没有相关联想词时确定不显示,但用户点击输入框之外的位置时,联想词列表应该能够隐藏。若是采用将associateWords来隐藏的话,用户再次点击输入框时,会多向服务器发送一次搜索相关联想词的请求。axios
input (e) {
//keyup事件的event
e = e || window.event
this.showList = true
// 按“↑” 键
if (e.keyCode === 38) {
this.focusIndex--
this.selectedLi()
} else if (e.keyCode === 40) {
// 按“↓”,数组下标从0开始,list的[0]项内容为用户输入内容,不显示,从[1]项开始显示
this.focusIndex++
this.selectedLi()
} else if (e.keyCode === 13) {
// 按“Enter”调用搜索函数
this.doSomething() //----向后台发送搜索请求
this.associateWords = []
} else {
// 用户继续输入时,将inputContent置空----很是重要的一步
this.inputContent = ''
this.focusIndex = 0
// 搜索联想内容
this.getAssociateWords() //----向后台请求相关联想词列表
this.clearSelected()
}
}复制代码
与样式相关的js操做数组
selectedLi (hoverIndex) {
// 当inputContent内容为空时,记录下搜索框中用户输入的内容,做为associate-item的第一项
if (this.inputContent === '') {
this.inputContent = this.content
}
let associateList = document.getElementsByClassName('associate-item')
// 移除已添加的.selected样式
for (var i = 0; i < associateList.length; i++) {
associateList[i].classList.remove('selected')
}
if (hoverIndex !== undefined) {
this.focusIndex = hoverIndex
}
// 一直按向下键超出联想内容li长度时,应回到联想内容第一个
this.focusIndex = (this.focusIndex + associateList.length) % associateList.length
// 为选中的li添加.selected样式
let selectedOne = associateList[this.focusIndex]
this.content = selectedOne.textContent
selectedOne.classList.add('selected')
}
clearSelected () {
let associateList = document.getElementsByClassName('associate-item')
// 移除已添加的.selected样式
for (var i = 1; i < associateList.length; i++) {
associateList[i].classList.remove('selected')
}
}复制代码
为除了input框之外的页面部分添加监听事件,点击input之外的部分时,隐藏联想词列表服务器
// 点击input输入框之外的位置时 隐藏联想词列表
document.body.addEventListener('click', e => {
if (e.target.nodeName === 'INPUT') {
this.showList = true
} else {
this.showList = false
}
})复制代码
向后台服务器请求联想词列表函数
getAssociateWords () {
let self = this
axios.get('XX/data.json').then(function (res) {
self.associateWords = result.slice(0, 5)
})
}复制代码