vue输入框联想词功能

1.实现的功能

输入框输入字符后,联想词列表出现,能够按“↓”或“↑”选择列表中的内容,也能够鼠标点选,且互相不影响选择样式,即只会出现一个被选中,“Enter”键发起检索。javascript

2.DOM结构

<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>复制代码

3.变量

content: '',---双向绑定的数据,input输入框中的内容,用户选择联想词列表时同步变化
inputContent: '',---保存用户经过键盘输入的内容,不与用户选择的联想词列表同步变化
focusIndex: 0,---用户选择的联想词<li>列表的下标
associateWords: [],---联想词列表数组
showList: true---是否显示联想词列表由此变量和associateWords的长度共同控制

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

4.JavaScript部分

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) 
    })    
}复制代码
相关文章
相关标签/搜索