對於開發人員來說,將進階編輯功能整合至網頁應用程式並不容易。網路平台只要使用 <input>
和 <textarea>
等元素,或是將 contenteditable
屬性套用至元素,即可編輯純文字和 HTML 文件。不過,這些元素類型的基本功能通常不足以滿足開發人員的應用程式需求。
開發人員往往最終會導入專屬的自訂編輯器檢視畫面,可以導入使用者所需的功能。編輯器檢視畫面可能會使用複雜的 DOM (甚至是 <canvas>
元素) 建立,但由於開發人員唯一在接收文字輸入內容的方式,就必須有聚焦於焦點的可編輯元素,因此仍需在網頁某處放置隱藏的 contenteditable
元素。
因此,雖然使用者似乎是直接編輯應用程式自訂編輯器檢視畫面中的內容,但開發人員其實可以在隱藏元素中收到含有事件處理常式的輸入內容,然後將輸入內容鏡像到可見的編輯器檢視畫面。這可能會造成問題,因為開發人員最終會對隱藏的 contenteditable
元素中瀏覽器預設的編輯行為造成影響。
為瞭解決這層問題,Microsoft Edge 團隊推動了 EditContext 的標準化作業。EditContext 的標準化為新的網路平台 API,可讓開發人員直接接收文字輸入內容,而不必與瀏覽器的預設編輯行為連結。
實際範例
例如使用者透過 Word Online 協作。使用者可以共同編輯,查看彼此的變更和遊標位置。然而,如果其中一位協作者使用「輸入法編輯器 (IME)」視窗撰寫日文,這是因為如果在含有有效輸入法編輯器的組合中,變更正在編輯的 DOM 區域,可能會導致組合提早取消。應用程式必須等到輸入法編輯器視窗關閉後才能更新檢視畫面,造成延遲並阻礙協作。
為提供更優質的開發人員和使用者體驗,開發人員需要設法在 HTML DOM 檢視中分離文字輸入內容。EditContext API 是這個問題的解決方案,
EditContext 的基本概念
透過 EditContext,您可以直接透過 EditContext API 介面接收文字和組合輸入,而非觀察 DOM 對 DOM 的變更。這可讓您更嚴格地控制輸入內容的處理方式,甚至可以編輯 <canvas>
元素。
將 EditContext 執行個體與元素建立關聯,即可編輯該例項:
// This will be our editable element.
const element = document.querySelector('#editor-element');
// Creating the EditContext object.
const editContext = new EditContext();
// Associating the EditContext object with our DOM element.
// The element is now focusable and can receive text input.
element.editContext = editContext;
// In order to render the text typed by the user onto the
// page, as well as the user's selection, you'll need to
// receive the input in a textupdate event callback.
editContext.addEventListener('textupdate', event => {
element.textContent = editContext.text;
// For brevity, the code to render the selection
// isn't shown here.
renderSelection(event.selectionStart, event.selectionEnd);
});
作者的責任
使用 EditContext API 有助於支援進階輸入法,例如輸入法編輯器組合視窗、表情符號挑選器和其他作業系統輸入途徑。為了在可編輯的元素中實現上述功能,EditContext API 需要一些資訊。除了轉譯文字和選取內容之外,使用 EditContext API 時還需要進行其他操作。
管理可編輯區域的一側,或是使用者的選擇變更
呼叫 updateControlBounds()
和 updateSelectionBounds()
方法,在可編輯區域的大小或使用者選項的變更時通知 EditContext 執行個體。這有助於平台決定顯示輸入法編輯器視窗,以及其他平台專用的編輯 UI。
// It's necessary to provide bounds information because EditContext
// is generic enough to work with any type of web editor, even
// <canvas>-based editors. The API doesn't make any assumptions as
// to how the editor is implemented or how the selection is rendered.
// Bounds are given in the client coordinate space.
const controlBound = editorElement.getBoundingClientRect();
const selection = document.getSelection();
const selectionBound = selection.getRangeAt(0).getBoundingClientRect();
editContext.updateControlBounds(controlBound);
editContext.updateSelectionBounds(selectionBound);
管理編輯器 UI 的位置
監聽 characterboundsupdate
事件並呼叫 updateCharacterBounds()
以協助平台判斷要在哪裡顯示輸入法編輯器視窗,以及其他平台專用的編輯 UI。
正在套用格式設定
監聽 textformatupdate
事件,然後將事件指定的格式設定套用至編輯器檢視畫面。撰寫特定語言時,IME 會使用這些文字裝飾。舉例來說,日文輸入法編輯器會使用底線,表示正在撰寫文字的哪些部分。
處理 RTF 格式編輯行為
監聽 beforeinput
事件,則可處理任何您需要支援的 RTF 文字編輯行為,例如將文字變為粗體或斜體的快速鍵,或是套用拼字檢查校正功能。
管理使用者選取中的變更
如果使用者的選擇因鍵盤或滑鼠輸入而變更,您需要通知 EditContext 變更。這是因為 EditContext API 的用途廣泛,包括以 <canvas>
元素轉譯的編輯器,導致瀏覽器無法自動偵測選取變更。
document.addEventListener('selectionchange', () => {
const selection = document.getSelection();
// EditContext doesn't handle caret navigation, so all the caret navigation/selection that happens
// in DOM space needs to be mapped to plain text space by the author and passed to EditContext.
// This example code assumes the editable area only contains text under a single node.
editContext.updateSelection(selection.anchorOffset, selection.focusOffset);
});
如果您使用的 EditContext 是 <canvas>
元素,您也需要實作選取和插入點導覽行為,例如使用方向鍵透過文字瀏覽。此外,瀏覽器內建的拼字檢查功能僅適用於非 <canvas>
元素。
EditContext 與 contenteditable
如果您要導入功能完善的編輯器,且想要完全掌控文字輸入的處理方式,或是需要新增進階功能 (例如與多位使用者共同編輯),就很適合使用 EditContext。不過,鑒於上述的 EditContext 使用規定,如果只需要支援簡單的文字編輯功能,建議你還是使用 <input>
、<textarea>
元素或 contenteditable
屬性。
展望未來
Microsoft Edge 團隊與 Chrome 工程師合作,在 Chromium 中實作 EditContext,且預計在 Chrome 和 Edge 的 121 版 (2024 年 1 月) 發布。目前只有以 Chromium 為基礎的瀏覽器支援,但可以在 EditContext API 上閱讀 Mozilla 和 WebKit 的位置。
我們希望讓網頁程式開發人員能夠更輕鬆地建立強大的自訂網路編輯體驗,而我們相信 EditContext API 能解決現有的挑戰,並提供更直接地處理文字輸入的方式,進而達成這個目標。
如要進一步瞭解 API,請參閱 MDN 說明文件。如要提交對 API 設計的意見回饋,請在 EditContext API 的 GitHub 存放區中開啟問題。如要回報實作 API 的相關錯誤,請前往 crbug.com 提交錯誤。