文章分享了,怎样一步步优化输入框的监听事宜,以到达兼容、高效和组合输入友爱等目标。
完美的输入框监听计划
keyup
监听输入框的输入,最原始的要领是运用keyup
事宜。
不运用change
事宜,它只会在输入框落空核心后被触发。
此体式格局兼容性广,但效力较低,毕竟恣意的按键都邑触发该事宜。
<input id="input" type="text" />
<script>
document.querySelector('#input')
.addEventListener('keyup', function() {
console.log('value:', this.value);
});
</script>
input
我们只愿望当值发作变化后再触发监听,如许,input
事宜涌现了。
它只会在输入框的值发作变化后被触发,不过IE8及以下不支撑该事宜。
网上很多人运用IE独占的propertychange
事宜,作为替换input
的计划,这里不引荐。
一方面它会在恣意属性值变化后被触发,没有专一性,不够语义,比较糟蹋。
二方面网上都是用jQuery
等东西库操纵,比较简单,而我们的目标是用原生代码完成。
三方面是不支撑input
事宜的浏览器已很少了,硬碰上了就用keyup
应付。
<input id="input" type="text" />
<script>
document.querySelector('#input')
.addEventListener('input', function() {
console.log('value:', this.value);
});
</script>
接着上步,怎样在不支撑input
事宜时运用keyup
事宜呢?
直接检测事宜不太靠谱,能够应用input
在keyup
之前发作的性子,奇妙的完成此功用。
<input id="input" type="text" />
<script>
let inInputEvent = false;
let input = document.querySelector('#input');
input.addEventListener('keyup', function() {
if (inInputEvent) {
// You can remove keyup listener.
} else {
console.log('keyup:', this.value);
}
});
input.addEventListener('input', function() {
if (!inInputEvent) inInputEvent = true;
console.log('input:', this.value);
});
</script>
耽误函数
在搜刮功用中,理想化的情形是当用户悉数输入后,再马上实行搜刮。
那末题目来了,怎样在不须要用户点击搜刮按钮的情况下,得知其历程的完成呢?没有办法。
虽然没有办法,但有优化的体式格局:假定用户每一个单词的输入距离,以此时间耽误实行搜刮功用。
英文平常为 300ms ,中文可设置成 500ms 。
<input id="input" type="text" />
<script>
let input = document.querySelector('#input');
let trigger = createDelayFunction(console.log);
input.addEventListener('input', function() {
trigger(this.value);
});
function createDelayFunction(fn, timeout = 300) {
let timeoutId = -1;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn.apply(null, args);
}, timeout);
}
}
</script>
composition
中文、日文等须要借助输入法组合输入,即便是英文,如今也可借助组合输入举行选词等。
现实中,我们愿望将用户组合输入完的一段笔墨,而不是每输入一个字母,算做一次输入的完成。
组合输入事宜应运而生,经常使用的是compositionstart
(组输最先)和compositionend
(组输完毕)事宜。
连系组合事宜不监听一般的输入,以及compositionstart
发作在input
事宜之前,能够云云优化中文输入。
<input id="input" type="text" />
<script>
let inCompositionEvent = false;
let input = document.querySelector('#input');
input.addEventListener('input', function() {
!inCompositionEvent && console.log('input', this.value);
});
input.addEventListener('compositionstart', function() {
inCompositionEvent = true;
});
input.addEventListener('compositionend', function() {
inCompositionEvent = false;
console.log('composition', this.value);
});
</script>
连系
末了是连系以上几步天生一个融会要领,代码加示例:地点。
内里还做了些加强:
比方监听函数返回的是一个,移除这一步所加的一切事宜的要领。
比方设置是不是监听组合输入事宜,由于好的搜刮框会直接依据拼音最先搜刮,无需比及汉字的构成。
代码运用ES6语法,需运用支撑ES6的浏览器(Chrome最新版)或转码后才运用,体谅。
function listenInput(dom, callback, {
timeout = 300,
useCompositionEvent = true
} = {}) {
let value = '';
let inInputEvent = false;
let inCompositionEvent = false;
let trigger = createDelayFunction(valueChanged, timeout);
// Return a function that can remove listeners added here.
return enabledEvent(dom);
function valueChanged(val) {
if (val === value) {
return ;
} else {
value = val;
}
callback(value, { dom: dom });
}
function enabledEvent(dom) {
dom.addEventListener('keyup', keyup);
dom.addEventListener('input', input);
useCompositionEvent && dom.addEventListener('compositionstart', compositionstart);
useCompositionEvent && dom.addEventListener('compositionend', compositionend);
return function() {
dom.removeEventListener('keyup', keyup);
dom.removeEventListener('input', input);
useCompositionEvent && dom.removeEventListener('compositionstart', compositionstart);
useCompositionEvent && dom.removeEventListener('compositionend', compositionend);
};
function keyup() {
if (inInputEvent) {
dom.removeEventListener('keyup', keyup);
} else {
trigger(this.value);
}
}
function input() {
if (!inInputEvent) inInputEvent = true;
if (!inCompositionEvent) trigger(this.value);
}
function compositionstart() {
inCompositionEvent = true;
}
function compositionend() {
inCompositionEvent = false;
trigger(this.value);
}
}
}
function createDelayFunction(fn, timeout = 300) {
let timeoutId = -1;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn.apply(null, args);
}, timeout);
}
}