隆重推出使用 EditContext API 建立自訂網頁編輯體驗的新方法

開發人員在網頁應用程式中加入進階編輯功能,並非一項簡單的工作。網頁平台可讓使用者編輯純文字和 HTML 文件,方法是使用 <input><textarea> 等元素,或將 contenteditable 屬性套用至元素。不過,這些元素類型的基本功能通常不足以滿足開發人員在應用程式中想要達成的目標。

開發人員通常會實作自己的自訂編輯器檢視畫面,以便實作使用者需要的功能。編輯器檢視畫面可能會使用複雜的 DOM 建構,甚至是 <canvas> 元素,但由於開發人員接收文字輸入的唯一方式需要聚焦於可編輯的元素,因此他們仍需要在網頁中某處放置隱藏的 contenteditable 元素。

結果是,雖然使用者似乎直接在應用程式的自訂編輯器檢視畫面中編輯內容,但開發人員實際上是在隱藏元素中使用事件處理常式接收輸入內容,然後將其鏡像顯示在可見的編輯器檢視畫面中。這可能會導致問題,因為開發人員最終會與瀏覽器在隱藏 contenteditable 元素中的預設編輯行為發生衝突。

為解決這類問題,Microsoft Edge 團隊推動了 EditContext 的標準化,這是一種新的網路平台 API,可讓開發人員直接接收文字輸入內容,而不會受到瀏覽器預設編輯行為的限制。

實際範例

舉例來說,當使用者在 Word Online 中協作時。使用者可以共同編輯,並查看彼此的變更內容和游標位置。不過,如果其中一位協作者使用輸入法編輯器 (IME) 視窗編寫日文文字,則編輯器不會更新,直到 IME 使用者完成編寫作業後,才會顯示其他使用者的變更內容。這是因為在有有效 IME 組合時,對所編輯的 DOM 區域進行變更,可能會導致組合提前取消。應用程式必須等到 IME 視窗關閉後,才能更新檢視畫面,這可能會造成延遲,並妨礙協同作業。

在撰寫文字時,無法在 Word Online 中協作

為了提供更優質的開發人員和使用者體驗,開發人員需要一種方法,將文字輸入與 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 可更輕鬆地支援進階輸入方法,例如 IME 組合視窗、表情符號挑選器和其他作業系統輸入介面。如要在可編輯元素中執行上述所有操作,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 會使用底線,顯示文字的哪個部分正在輸入。

輸入法編輯器視窗的螢幕截圖,用於輸入日文字元。

處理 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 為基礎的瀏覽器,但您可以參閱 MozillaWebKit 的 EditContext API 位置。

我們希望讓網頁開發人員更輕鬆地在網頁上打造強大的自訂編輯體驗,而 EditContext API 可解決現有挑戰,並提供更直接的方式處理文字輸入,因此我們認為這項 API 可達成這項目標。

如要進一步瞭解 API,請參閱 MDN 說明文件。如要針對 API 設計提交意見回饋,請在 EditContext API 的 GitHub 存放區中提出問題。如要回報 API 實作錯誤,請前往 crbug.com 提交錯誤。