真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

中文輸入法中光標(biāo)跟隨能力觸發(fā)的瀏覽器事件探究


:::tip
最近在著手騰訊文檔的輸入體驗優(yōu)化,在其中有一個不起眼的小需求引起了我的注意,并順便研究了一些事件監(jiān)聽機(jī)制相結(jié)合的特點,特此記錄一下填坑過程。
:::

10余年的羅田網(wǎng)站建設(shè)經(jīng)驗,針對設(shè)計、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時及時工作處理。營銷型網(wǎng)站建設(shè)的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整羅田建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計,從而大程度地提升瀏覽體驗。創(chuàng)新互聯(lián)從事“羅田網(wǎng)站設(shè)計”,“羅田網(wǎng)站推廣”以來,每個客戶項目都認(rèn)真落實執(zhí)行。

模擬光標(biāo)跟隨

大部分的主流輸入法都有這樣一個特性,在輸入中文時,可以通過左右方向鍵控制光標(biāo),移動至輸入?yún)^(qū)中任意兩個字符之間的位置,用戶接下來的字符輸入將在光標(biāo)處直接插入。

由于騰訊文檔的渲染的畫布是完全自主實現(xiàn)的,為了在體驗上與普通可編輯畫布保持一致,我們需要自己來模擬這一光標(biāo)的移動行為。

首先,我們需要確定的是輸入法中的模擬光標(biāo)進(jìn)行更新的時機(jī)。經(jīng)試驗,用戶在進(jìn)行中文輸入時,若使用了方向鍵移動光標(biāo),將會觸發(fā)光標(biāo)的移動行為。因此,首先要解決的是使用合適的事件監(jiān)聽來捕獲這一行為,從而進(jìn)行更新。既然是對輸入框的行為進(jìn)行模擬,自然而然的,我們首先想到的是輸入框觸發(fā)的監(jiān)聽器。

瀏覽器輸入框?qū)斎氲谋O(jiān)聽機(jī)制

在瀏覽器對鍵盤的輸入規(guī)范中,將鍵盤輸入分為了直接輸入與間接輸入兩種。直接輸入將會觸發(fā)輸入框的 onInput 事件 (IE9 之前不支持該事件,只能用 onKeyUp 等鍵盤事件作為降級選擇)。而對于間接輸入,規(guī)范將事件監(jiān)聽分為了 onCompositionStart, onCompositionUpdate, onCompositionEnd 三個部分。

而間接輸入的同時,中間態(tài)的寫入也會導(dǎo)致輸入框內(nèi)容的變化,從而也會觸發(fā) onInput 事件。因此在間接輸入中,事件的觸發(fā)次序為:onCompositionStart, onCompositionUpdate, onInput, onCompositionEnd。

需要注意的是,若輸入完成時,輸入框的內(nèi)容沒有發(fā)生變化,則 onChange 事件與 onCompositionEnd 事件都將不會被觸發(fā)。

中文輸入法在鍵入選詞的過程屬于間接輸入情況,此時中間文本不會直接落盤在輸入框內(nèi)。而通過回車等按鍵退出中文輸入選詞后,中文文字將會落盤到輸入框,此時屬于直接輸入情況。

而我們需要關(guān)注的光標(biāo)事件顯然是在間接輸入中獲取到的。在輸入法選詞光標(biāo)左右移動時,由于內(nèi)容不變,此時并不會觸發(fā) onInput 事件,但是會觸發(fā)一次 onCompositionUpdate 事件,我們可以通過這個事件來判斷光標(biāo)位置,重置畫布的光標(biāo)位置。但最終我們并未使用這個事件做判斷器,原因在下面會講到。

判斷當(dāng)前光標(biāo)的位置

解決了了光標(biāo)的重置時機(jī),接下來就該解決光標(biāo)的位置判定了。由于 DOM 標(biāo)準(zhǔn)中并沒有直接獲取光標(biāo)位置的方法,因此這一塊也需要我們自主實現(xiàn)。我的思路是,通過選取光標(biāo)到輸入起始位置的字符串,判斷選中的字符串長度,即可知道光標(biāo)當(dāng)前位置相對于起始位置的偏移量,從而確定光標(biāo)位置。

對于普通的 input 輸入框來說起始比較簡單,輸入框提供了 inputElement.selectionStart 屬性作為當(dāng)前光標(biāo)位置距離輸入起始點的偏移量,我們直接使用就可以了。但是對于 contentEditable=true 的 div 節(jié)點來說是沒有這一屬性的,我們得另想辦法。

根據(jù)之前寫 E2E 測試得來的靈感,我們可以模擬創(chuàng)建一個從當(dāng)前光標(biāo)位置到輸入起始位置的選區(qū),通過判斷該選區(qū)的字符串長度即光標(biāo)所在位置的偏移量。通過 window.getSelection() 方法能夠得到 Selection 對象,這是一個表示當(dāng)前文本選區(qū)的對象,由于我們正處在輸入狀態(tài)中,因此該選區(qū)位置就在當(dāng)前的輸入框中,從而能獲取到上面所需的偏移量。

const selection = window.getSelection();
// 確定輸入框在輸入態(tài),存在選區(qū)
if (selection.rangeCount > 0) {
    const range = selection.getRangeAt(0);
    return range.endOffset;
}

獲取完光標(biāo)位置,還需要在我們的畫布上重新設(shè)置回去。設(shè)置的思路其實是類似的,通過使用document.createRange方法新建一個選區(qū)范圍,其起始位置設(shè)置為需要移動的目標(biāo)位置,然后移除選區(qū),即可使光標(biāo)落在目標(biāo)位置了。

性能優(yōu)化

之前說到在光標(biāo)移動時的確會觸發(fā)一次onCompositionUpdate 事件。但是,onCompositionUpdate 事件是一個高頻的操作,每一次間接輸入時都會觸發(fā),這會導(dǎo)致光標(biāo)不斷地重置位置,帶來不必要的性能損失。

并且,onCompositionUpdate 事件的入?yún)⒅挥懈碌闹虚g字符串值,只能用來判斷輸入中間字符串是否發(fā)生變化。移動光標(biāo)行為本身并不會導(dǎo)致字符串發(fā)生改變,但反過來,使字符串不發(fā)生改變的操作一定是移動光標(biāo)操作這一說法并不成立。因此,盡管移動光標(biāo)會觸發(fā)該事件,但我們?nèi)匀粵]有有效的手段去判斷是輸入法中的光標(biāo)移動導(dǎo)致的事件觸發(fā)。

那么,之前用很大篇幅講過光標(biāo)變動的本質(zhì)實際上是選區(qū)變化,那么,輸入法觸發(fā)的光標(biāo)移動會不會給輸入框發(fā)出選區(qū)變更通知呢?很不幸,目前絕大多數(shù)的輸入法都是不支持的。并且由于光標(biāo)移動被視為輸入法內(nèi)部的行為,因此在輸入框中光標(biāo)所進(jìn)行的移動,不會有事件主動拋出。因此,輸入框中的選區(qū)變更事件 onSelectionChange 事件也無法被觸發(fā)。

既然輸入框中的事件監(jiān)聽無法準(zhǔn)確判斷光標(biāo)的移動,我們只能退而求其次,從更低層次的邏輯,通過監(jiān)聽鍵盤的按鍵輸入來嘗試還原這一行為了。優(yōu)化思路是這樣的,觸發(fā)光標(biāo)跟隨的時機(jī)規(guī)則為:用戶輸入時,若使用了左方向鍵移動光標(biāo),將會開啟光標(biāo)跟隨的能力,隨著輸入不斷更新的光標(biāo)位置,直到光標(biāo)再次被移動到末尾位置結(jié)束。由于中文輸入時按下左方向鍵的行為是一個低頻操作,這樣一來,大部分的輸入操作都不需要執(zhí)行判斷并重置光標(biāo),提高普通輸入下的性能表現(xiàn)。

附上最終的判斷邏輯吧:

那么,如何獲取并判斷用戶輸入時的按鍵信息呢?當(dāng)然是使用更第一層級的事件接口 KeyboardEvent 了。

鍵盤輸入事件對中文輸入法的支持

KeyboardEvent 在低層級下提示用戶與一個鍵盤按鍵的交互是什么,不涉及這個交互的上下文含義。一般來說當(dāng)你需要處理文本輸入的時候,應(yīng)當(dāng)使用上節(jié)所說的輸入框監(jiān)聽事件代替。例如當(dāng)用戶使用其他方式輸入文本時,如平板電腦的手寫系統(tǒng)等,鍵盤事件可能不會觸發(fā)。

KeyboardEvent 對象描述了用戶與鍵盤的交互。 每個事件都描述了用戶與一個按鍵(或一個按鍵和修飾鍵的組合)的單個交互;事件類型 keydown,keypress 與 keyup 用于識別不同的鍵盤活動類型。

鍵盤輸入事件的設(shè)計思路與間接輸入的鉤子類似,瀏覽器中對于鍵盤輸入同樣分為 onKeyDown, onKeyPress, onKeyUp 三個階段的事件觸發(fā),分別對應(yīng)按鍵不同的行為觸發(fā)時機(jī)。(注:onKeyPress 事件高度依賴設(shè)備支持,所以盡量不要使用該鉤子)

這三個事件都傳入了 KeyboardEvent 入?yún)ⅲ瑤椭覀兞私猱?dāng)前執(zhí)行該事件時觸發(fā)的按鍵信息。MDN 上該入?yún)⒕哂腥缦聦傩灾С郑?/p>

在文檔規(guī)范中,我們可以發(fā)現(xiàn)許多對問題的解決十分有用的新屬性,例如 event.isComposing 屬性用于判斷當(dāng)前是否會觸發(fā) onCompositionUpdate 事件,event.code 用于判斷與鍵盤布局與輸入狀態(tài)無關(guān)的當(dāng)前按鍵輸入,獲取中文輸入中的按鍵輕而易舉。我們可以利用這兩個狀態(tài)幫助我們完成按鍵監(jiān)聽與事件觸發(fā)。

兜底方案支持

之前說過, KeyboardEvent 是一個十分依賴軟硬件支持的事件,不僅需要瀏覽器的能力支持,與輸入法甚至鍵盤類型都有關(guān)系。經(jīng)試驗后發(fā)現(xiàn),這些新屬性在許多瀏覽器與輸入法的組合中都無法通過onKeyDown正確獲取,在 Windows 下部分中文輸入法甚至都無法支持 event.key 屬性。為了達(dá)到最大的兼容性,在兜底的方法下,僅能用 event.keyCode 這種已經(jīng)被 deprecated 的方法來勉強(qiáng)替代使用了。

兜底方案的使用問題就此解決了嗎?并沒有。中文拼音的輸入中間字符是系統(tǒng)無法識別的。在 Windows 桌面應(yīng)用程序?qū)︽I盤輸入規(guī)范中,我們發(fā)現(xiàn) Windows 將所有未識別的設(shè)備輸入都設(shè)置為 VK_PROCESSKEY 229,瀏覽器的 event.keyCode 復(fù)用了這一規(guī)范,因此在中文輸入過程中,無論按下什么按鍵,返回的 event.keyCode 永遠(yuǎn)是 229。

網(wǎng)上對于該問題的解決方案都是建議使用 onKeyUp 代替 onKeyDown。但首先,這不滿足對于一個要求實時體現(xiàn)輸入的光標(biāo)移動操作要求。第二,使用 onKeyUp 會有更多的問題,在 Windows 下進(jìn)行中文輸入時,由于不同的輸入法回調(diào) onKeyUp 的實現(xiàn)不同,該事件可能會被觸發(fā)一次或兩次,要么全為 229,要么一次為 229,另一次為正確的 key(對,說的就是你,搜狗)。為了避免我們?nèi)ゲ粩嗳ヌ钗寤ò碎T的第三方輸入法實現(xiàn)的坑,兜底方案采用了當(dāng)檢測到輸入了未識別的按鍵時,也啟用光標(biāo)跟隨能力。

結(jié)語

一套操作下來,這套中文輸入法下光標(biāo)跟隨的功能算是完美實現(xiàn)了?;仡櫼幌挛覀兘鉀Q這個問題所趟過的坑,實際上也反映著瀏覽器 JS DOM 標(biāo)準(zhǔn)在不斷進(jìn)化,不斷補足歷史遺留的坑點。當(dāng)然,它還遠(yuǎn)遠(yuǎn)稱不上完美,仍然存在大量的能力缺失,如我們在這個問題中遇到的判斷光標(biāo)偏移量的解決方案,本質(zhì)上還是一種 hack。而擴(kuò)展 JS 的能力邊界,使其變得更強(qiáng)大,更好用,這正是我們作為前端開發(fā)人員需要努力的方向。


當(dāng)前名稱:中文輸入法中光標(biāo)跟隨能力觸發(fā)的瀏覽器事件探究
標(biāo)題URL:http://weahome.cn/article/dsojijp.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部