對開發人員來說,將進階編輯功能整合到網頁應用程式並不容易。網頁平台可讓使用者編輯純文字和 HTML 文件,方法是使用 <input>
和 <textarea>
等元素,或將 contenteditable
屬性套用至元素。不過,這些元素類型的基本功能通常不足以讓開發人員在應用程式中實現目標。
開發人員通常會實作自有的自訂編輯器檢視畫面,以便實作使用者需要的功能。編輯器檢視畫面可能會使用複雜的 DOM 建構,甚至是 <canvas>
元素,但由於開發人員接收文字輸入的唯一方式需要聚焦於可編輯的元素,因此他們仍需要在網頁中某處放置隱藏的 contenteditable
元素。
結果是,雖然使用者似乎直接在應用程式的自訂編輯器檢視畫面中編輯內容,但開發人員實際上是在隱藏元素中使用事件處理常式接收輸入內容,然後將其鏡像顯示在可見的編輯器檢視畫面中。這可能會導致問題,因為開發人員最終會與瀏覽器預設的編輯行為在隱藏的 contenteditable
元素中發生衝突。
為解決這類問題,Microsoft Edge 團隊推動了 EditContext 的標準化,這是一種新的網路平台 API,可讓開發人員直接接收文字輸入內容,而不會受到瀏覽器預設編輯行為的限制。
實際範例
舉例來說,當使用者在 Word Online 中協作時。使用者可以共同編輯,並查看彼此的變更和遊標位置。不過,如果其中一位協作者使用輸入法編輯器 (IME) 視窗編寫日文文字,則編輯器不會更新,直到 IME 使用者完成編寫作業後,才會顯示其他使用者的變更內容。這是因為在有有效 IME 組合時,對所編輯的 DOM 區域進行變更,可能會導致組合提前取消。應用程式必須等到 IME 視窗關閉後,才能更新檢視畫面,這可能會導致延遲並妨礙協作。
為了提供更優質的開發人員和使用者體驗,開發人員需要一種方法,將文字輸入與 HTML DOM 檢視畫面分離。解決這個問題的方法是使用 EditContext API。
EditContext 基本概念
有了 EditContext,您就能直接透過 EditContext API 途徑接收文字和組合輸入內容,而非透過觀察 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 例項。這有助於平台決定要顯示 IME 視窗和其他平台專屬編輯 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()
回應,協助平台決定在何處顯示 IME 視窗和其他特定平台的編輯 UI。
正在套用格式設定
監聽 textformatupdate
事件,並將事件指定的格式套用至編輯器檢視畫面。在編寫特定語言時,IME 會使用這些文字裝飾。舉例來說,日文 IME 會使用底線,顯示文字的哪個部分正在輸入。
處理 RTF 格式編輯行為
監聽 beforeinput
事件,處理您想支援的任何富文字編輯行為,例如用來加粗或斜體文字的快速鍵,或套用拼字檢查修正。
管理使用者選取中的變更
如果使用者因鍵盤或滑鼠輸入而變更選取項目,您必須通知 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 為基礎的瀏覽器,但您可以參閱 Mozilla 和 WebKit 的 EditContext API 位置。
我們希望協助網頁程式開發人員更輕鬆地在網路上打造強大的自訂編輯體驗。我們相信 EditContext API 能夠解決現有難題,並提供更直接的方式處理文字輸入內容。
如要進一步瞭解 API,請參閱 MDN 說明文件。如要針對 API 設計提交意見回饋,請在 EditContext API 的 GitHub 存放區開啟問題。如要回報 API 實作錯誤,請前往 crbug.com 提交錯誤。