这个功能主要是实现了一个可以实时查询结果的搜索框,并具备点击外部关闭搜索结果框体的功能,除了v-show和transition依托于vue实现以外其余功能都基于原生JS实现。
效果图:
该功能的实现主要是很久之前面试被问到过,当时没有做出来,现在兜兜转转又碰到了这个需求,索性一次性写完整,刚好新学到了composition
系列事件,
首先解释一下composition
系列事件,这个系列事件分为三种类型
conpositionstart
在MDN中,对该事件的定义为:文本合成系统(输入法编辑器)开始新的输入合成时会触发,通俗来说就是在中文输入法开始输入时,该事件就会触发,例图如下
conpositionupdate
该事件是文本合成系统更新输入时会触发,例图如下
conpositionend
该事件在MDN中的定义是当文本段落的组成完成或取消时(敲下回车或者空格),compositionend 事件将被触发,例图如下
敲击回车后
实现思路:既然可以获取到用户输入的三种状态,那么就需要声明一个变量用来保存输入状态,最后在监听Input事件的回调中,根据输入状态来实现功能。而且在调试过程中发现,同为同步任务的情况下,compositionend
事件的触发是要先于input
事件的,那么就表明,在input事件触发的时候,一定可以通过compositionend
来将用户的输入状态更新为最新状态。这就给开发带来了很大的便利。
使用composition
系列事件不同于监听input
事件+防抖的地方在于,如果是正在输入的情况,那么input事件在延时到期后就会触发而不会考虑到此时用户是否已经完成输入而需要触发,而composition
事件在没有选择备用词或者没有按下空格/回车是不会触发的,从状态管理的角度来看,状态之间的转移更为清晰可控。
该功能主要针对的点还是在于调起文本合成器输入的情况,如果是普通的连续输入数字/英文/英文+数字的场景,则只用基本的防抖+监听input即可。
HTML部分
<van-field>
<template #input>
<label>
<input name="projectName">
</label>
</template>
</van-field>
<transition name="van-fade">
<div v-show="visible" class="projectList">
<div class="van-ellipsis listItem" v-for="item in 8">
项目{{item}}
</div>
</div>
</transition>
JS部分,Vue只需要声明一个变量则不再赘述
const inputDom = document.querySelector('input[name="projectName"]')
let typing = false // 初始化为false,保证用户在连续输入英文/数字的情况下也可以正常进入input逻辑
function handleEvent(event) {
if (event.type === 'compositionstart') {
typing = true
}
if (event.type === 'compositionend') {
console.log('compositionend触发了')
// 可以在这里给备选list赋值
typing = false
}
if (event.type === 'input' && typing === false) {
console.log(event.target.value);
console.log('input触发了')
// 可以在这里调用接口
vm.projectData.name = inputDom.value
vm.visible = !!event.target.value
}
if (event.type === 'click' && !!inputDom.value) {
vm.visible = true
}
}
inputDom.addEventListener("input", debounce(handleEvent, 1000));
inputDom.addEventListener("compositionstart", handleEvent);
inputDom.addEventListener("compositionend", handleEvent);
inputDom.addEventListener('click', handleEvent)
document.addEventListener('click', (e) => {
if (!inputDom.contains(e.target)) {
vm.visible = false
}
})