需求
最近需要根据输入的文本来过滤列表。这是一个非常常见的要求。所以大概的代码是:
<template>
<div id="app">
<input type="text" :value="filterText" @input="onInput" />
<ul>
<li v-for="item in filteredList" :key="item">
{{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {
filterText: "",
list: [
"爱与希望",
"花海",
"Mojito",
"最长的电影",
"爷爷泡的茶"
]
};
},
computed: {
filteredList() {
if (!this.filterText) {
return this.list;
}
return this.list.filter(item => item.indexOf(this.filterText) > -1);
}
},
methods: {
onInput(e) {
this.filterText = e.target.value;
}
}
};
</script>
监听输入字段中的输入事件,然后开始修改filteredList列表。
一切都是那么自然。
但是,如果输入中文,由于先出现拼音,所以在中文输入过程中触发过滤器的列表将为空,只有在最终出现中文时才会出现结果。
作文开头和结尾
于是我在网上查了一下,发现了两个事件:compositionstart
和compositionend
MDN:developer.mozilla.org/en-US/docs/…
当用户开始使用拼音输入法输入汉字时,会触发组合开始事件。当段落的组装完成或中止时,会触发组装事件。
也就是说,当我们开始中文输入时,会触发一个作文开始事件。在中文输入过程中,不再触发作文开始事件,最后在中文输入完成时,触发作文结束事件。
经过实验发现,输入中文时,compositio start是在input事件之前激活的。
有了这个前提,就好办了。我只需要标记lock
,当合成开始触发时,lock=true
,当合成结束触发时,lock=false
。仅在假锁的情况下才会执行对输入事件的过滤操作。
代码如下:
<template>
<div id="app">
<input type="text" :value="filterText"
@input="onInput"
@compositionstart="onCompositionStart"
@compositionend="onCompositionEnd"
/>
<ul>
<li v-for="item in filteredList" :key="item">
{{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {
filterText: "",
list: [
"爱与希望",
"花海",
"Mojito",
"最长的电影",
"爷爷泡的茶"
],
lock; false, // 打标
};
},
computed: {
filteredList() {
if (!this.filterText) {
return this.list;
}
return this.list.filter(item => item.indexOf(this.filterText) > -1);
}
},
methods: {
onInput(e) {
if (!this.lock) {
this.filterText = e.target.value;
}
},
onCompositionStart() {
this.lock = true;
},
onCompositionEnd(e) {
this.filterText = e.data;
this.lock = false;
}
}
};
</script>
v型形状
上面的代码我们没有使用vue的v-model
双向绑定形式。如果您使用v-model
形式,您将看到输入中文时不会触发输入事件。
查看vue的源码src/platforms/web/runtime/directives/model.js
,有几行代码:
export default {
inserted (el, binding, vnode) {
if (vnode.tag === 'select') {
setSelected(el, binding, vnode.context)
el._vOptions = [].map.call(el.options, getValue)
} else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
el._vModifiers = binding.modifiers
if (!binding.modifiers.lazy) {
// Safari < 10.2 & UIWebView doesn't fire compositionend when
// switching focus before confirming composition choice
// this also fixes the issue where some browsers e.g. iOS Chrome
// fires "change" instead of "input" on autocomplete.
el.addEventListener('change', onCompositionEnd)
if (!isAndroid) {
el.addEventListener('compositionstart', onCompositionStart)
el.addEventListener('compositionend', onCompositionEnd)
}
/* istanbul ignore if */
if (isIE9) {
el.vmodel = true
}
}
}
}
}
//...
function onCompositionStart (e) {
e.target.composing = true
}
function onCompositionEnd (e) {
// prevent triggering an input event for no reason
if (!e.target.composing) return
e.target.composing = false
trigger(e.target, 'input')
}
function trigger (el, type) {
const e = document.createEvent('HTMLEvents')
e.initEvent(type, true, true)
el.dispatchEvent(e)
}
可以看到vue已经做了同样的操作,所以v-model为我们做了很多优化,这也是vue这么优秀的原因之一。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。