監聽輸入框的輸入,最原始的方法是使用keyup事件。
不使用change事件,它只會在輸入框失去焦點後被觸發。
此方式兼容性廣,但效率較低,畢竟任意的按鍵都會觸發該事件。瀏覽器
<input id="input" type="text" /> <script> document.querySelector('#input') .addEventListener('keyup', function() { console.log('value:', this.value); }); </script>
咱們只但願當值發生變化後再觸發監聽,這樣,input事件出現了。
它只會在輸入框的值發生變化後被觸發,不過IE8及如下不支持該事件。app
網上不少人使用IE獨有的propertychange事件,做爲替代input的方案,這裏不推薦。
一方面它會在任意屬性值變化後被觸發,沒有專注性,不夠語義,比較浪費。
二方面網上都是用jQuery等工具庫操做,比較簡單,而咱們的目的是用原生代碼實現。
三方面是不支持input事件的瀏覽器已經不多了,硬碰上了就用keyup對付。dom
<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>
中文、日文等須要藉助輸入法組合輸入,即使是英文,如今也可藉助組合輸入進行選詞等。
實際中,咱們但願將用戶組合輸入完的一段文字,而不是每輸入一個字母,算作一次輸入的完成。優化
組合輸入事件應運而生,經常使用的是compositionstart(組輸開始)和compositionend(組輸結束)事件。
結合組合事件不監聽普通的輸入,以及compositionstart發生在input事件以前,能夠如此優化中文輸入this
<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>
最後是結合以上幾步生成一個融合方法,代碼加示例:code
裏面還作了些加強:
好比監聽函數返回的是一個,移除這一步所加的全部事件的方法。
好比配置是否監聽組合輸入事件,由於好的搜索框會直接根據拼音開始搜索,無需等到漢字的造成。事件
代碼使用ES6語法,需使用支持ES6的瀏覽器(Chrome最新版)或轉碼後才能使用,諒解ip
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); } }