雖然要講解的知識點是通用的,可是仍是要介紹下個人應用場景和測試環境。javascript
個人應用是一塊使用Html Canvas開發的黑板,在黑板上實現簡單的文字編輯功能。html
使用Canvas實現文字編輯器的細節這裏就不講了,原理大體都相同。一個必要條件是須要一個隱藏的textarea監聽文字輸入,由於canvas是沒法記錄文字選中、換行等信息的,也沒有辦法直接激活輸入法和軟鍵盤。textarea 以下:html5
複製代碼
不少狀況下,咱們須要實時監控文字輸入的變化,下面是幾種選擇。java
監聽keydown 和 keyup事件是最直接的方法,經過keyCode咱們能夠獲取按鍵值。chrome
this.hiddenTextArea.onkeyup = (e) => {
console.log("textKeyup",e.keyCode);
}
this.hiddenTextArea.onkeydown = (e) => {
console.log("textKeydown",e.keyCode);
}複製代碼
input事件在keydown事件觸發以後被觸發,這是input類型元素使用的標準事件,表示有文字輸入。canvas
從上圖中咱們能夠看到,觸發的事件爲InputEvent,從該事件對象的data屬性中能夠獲取到當前輸入的按鍵值。瀏覽器
正常狀況下,每一次按鍵都會觸發oninput事件。編輯器
該事件主要是爲了兼容IE9及如下瀏覽器對oninput事件的處理。測試
在監聽到 onpropertychange 事件後,可使用 event 的 propertyName 屬性來獲取發生變化的屬性名稱。ui
// Internet Explorer
function OnPropChanged (event) {
if (event.propertyName.toLowerCase () == "value") {
alert ("The new content: " + event.srcElement.value);
}
}複製代碼
咱們把及時響應鍵盤按鍵輸入單個按鍵表明的字符到文本框的行爲稱爲直接輸入模式,相對的非直接輸入模式,一般是輸入法攔截了按鍵消息以後的輸入,以中文輸入法爲例,一般是回車或者空格後完成輸入。
在非直接輸入模式下,咱們期待的結果是當用戶完成輸入的時候(按了空格或者回車鍵)觸發一次oninput事件。可是很不幸,每次按鍵都會觸發oninput事件,這會致使咱們不知道用戶正在輸入的是否是中文,也不知道何時結束的輸入,也就沒辦法及時對輸入的中文進行處理。 爲了解決這個問題,咱們看一下非直接輸入狀況下幾個有用的屬性和事件。
這兩個屬性是textarea對文字選中區域的標識,從0開始,簡化分析,咱們只考慮正常文字輸入,不考慮有選中的狀況,首先是直接輸入模式。
上圖是我在oninput事件中打的日誌,能夠明顯的看到每次oninput觸發以後,selectionStart和selectionEnd的值都相同並且表示最後一個文本,視覺上是咱們看到的光標所在的位置,若是咱們挪動光標,這兩個值也會變化。這裏咱們提取兩個關鍵特徵,在及時輸入的狀況下:
下面咱們再看非直接輸入模式。
從上圖中,咱們能夠看到在非直接輸入模式下,在未完成輸入以前,selectionStart一直爲0,selectonEnd隨着輸入一直變化。完成輸入時,selectionStart與selectonEnd值會相等。
從0.3.1的圖中咱們能夠看到拼音輸入法輸入過程當中,value值的變化,在完成輸入以前這個值是由輸入法控制的,完成以後,value的值會變爲輸入的文字內容。
這是一對事件,當非直接輸入開始第一個按鍵的時候,觸發compositionstart事件,非直接輸入結束的時候觸發compositionend事件, 在直接輸入狀況下,這兩個事件都不會觸發。咱們添加對這兩個事件的監聽:
this.hiddenTextArea.addEventListener('compositionstart', function () {
console.log("compositionstart");
that.isCompositionsting = false;
})
this.hiddenTextArea.addEventListener('compositionend', function () {
console.log("compositionend");
that.isCompositionsting = false;
})複製代碼
觀察上圖的輸出內容,各個事件的執行順序爲:
keydown-->compositionstart-->input-->keyup....-->input-->compositionend-->keyup。
如今咱們觀察下在非直接模式下,按鍵的值。
this.hiddenTextArea.onkeyup = (e) => {
console.log("keydowncode",e.keyCode);
}
this.hiddenTextArea.onkeydown = (e) => {
console.log("keyupcode",e.keyCode);
}複製代碼
經過上圖,咱們能夠看到不論你按下的是什麼鍵,keycode都被重置爲229了。固然這並非什麼標準,不一樣輸入法的行爲仍是不同的。不過目前咱們能接觸到的中文輸入法,正常狀況下都是229。
經過上面的分析,咱們從新整理下及時響應非直接輸入的思路。
下面以selectionStart 和 selectionEnd爲例,判斷中文輸入的開始和結束(非完整代碼)。
this.hiddenTextArea.oninput = (e) => {
if (textArea.selectionStart == textArea.selectionEnd) {
editor.bdCanvas.textEditor.currentText.text = textArea.value;
if (!isCompositionsting && 沒有文字選中狀況) {
//直接插入ascii字符
} else {
//插入文字(若是有部分文字被選中並替換)
//先刪除被選中的文字
if (isCompositionsting) {//中文(中文輸入法按空格以後,或者回車後輸入的字符)
isCompositionsting = false;
} else {//英文、數字 (有選中內容被替換)
}
}
} else {//到這兒說明輸入的是中文(中文輸入法未按空格以前)
isCompositionsting = true;
}
}複製代碼
上面的代碼引入了isCompositionsting變量,結合selectionStart和selectionEnd來作文字處理。其餘方法同理,這裏就不過多講解了。下面咱們來分析點異常狀況。
這是我安裝的最新的搜狗輸入法,下面要說的非正常狀況,只在這一個版本下會出現。
咱們在代碼中對keydown,keyup,input,compositionstart和compositionend同時作事件監聽,而後使用這個版本的搜狗輸入法作輸入。結果以下:
在未按下回車或者空格鍵以前,咱們看到:
再看結束輸入時的狀況:
上圖紅框內的內容爲結束輸入時的記錄,此時觸發一次input事件,selectionStart和selectionEnd相等。
這種狀況,結束輸入觸發一次input,我卻是認爲很合理的作法,這樣不少狀況咱們不用關心是不是中文輸入了,input的時候獲取新的value,記錄上一次的selectionstart就能夠了。
這種特殊行爲我沒有具體研究是輸入法自己的問題,仍是和瀏覽器、操做系統共同做用的結果。若是你編寫相似的程序,須要額外注意下。
本篇文章只是分析記錄了一些現象,並無什麼技術含量,歡迎留言討論。