⌨️

EditContext APIを理解する

2024/04/19に公開

https://developer.chrome.com/blog/introducing-editcontext-api

EditContext APIとはなんぞや

EditContext APIはChrome121版でリリースされた比較的新しい機能です。
現在執筆時点(2024/04/19)ではchromiumベース以外のブラウザではまだ実装されていないのでご注意ください。

caniuse

この機能は、一言で言うならばcontenteditableからDom Viewの機能を落としたようなものになっており、IMEやText Bufferに関する機能を提供してくれます。

contenteditableの何が辛かったのか?

contenteditableは、特定の要素を編集可能にしてくれる属性です。

ただし、近年のテキスト入力型のインターフェースの機能は多様化しており、contenteditable属性のついたtextareaだけでは表現が難しい場合があります。 
その場合、contenteditableのついた要素上で編集を行いつつ見た目のDOMは別で表示し、contenteditableのDOMは非表示にしたくなるでしょう。

この実装方針はバグを生みやすくアクセシビリティにも問題があるため、text bufferとDom Viewを疎結合にすることを可能にするインターフェースが必要になり、EditContext APIが誕生しました。

contenteditableが実際に問題になっている例

Real-world Examples of Text Input Issues in Top Sites and Frameworks

上記に実際に問題になっている例が挙げられています。上記からMonacoの例を見てみましょう。

Monacoはまさに入力時のみにcontenteditableなtexareaを用意する手法をとっており、その結果読み上げ時の単語にアウトラインがつかなかったりなどのアクセシビリティ的な問題が起きています。
参考:読み上げdemo video

実際にEditContext APIを使ってみる

EditContext の初期化の記法は下記です。

const editContext = new EditContext();
element.editContext = editContext;

実際に入力されたtext bufferのアクセスはeditContext.textでアクセスすることができます。
下記のようにelementのイベントにhookすることで、text bufferとDOM Viewの同期を実装することが可能です。

editContext.addEventListener("textupdate", (event) => {
  element.textContent = editContext.text;
});

また、編集可能領域の変更やユーザーのselectionの変更をeditContextに通知する必要があります。

const controlBound = element.getBoundingClientRect();
const selection = document.getSelection();
const selectionBound = selection?.getRangeAt(0).getBoundingClientRect();

editContext.updateControlBounds(controlBound); // 編集可能領域の変更通知
editContext.updateSelectionBounds(selectionBound); // selectionの変更通知

実際にEditContext APIを利用した、入力と出力のDOMを分離する実装サンプルを作成しました。

(キーボードインターフェースを介して入力用DOMに入力し、入力用DOMを介して出力DOMに出力し...なんだか不思議な気持ちになりますね)

おまけ:リッチテキスト界隈にどれぐらい影響があるのか

今回EditContext API調査を始めたきっかけはリッチテキストエディタにどれぐらいの影響があるかを知るためでした。
自分の感想としてはDom Viewと入力の分離がメインとなっており、各エディタの仕組みを大きく変えずに置換できるもののように感じました。
また、リッチテキストエディタ系のライブラリに関しては、多くはcontenteditableのついた要素をそのままDom Viewとしても扱っており、今回問題視されているようなことは起きていなさそうでした。
(仕方なく現状はcontenteditableのDOMを直接扱っている、という側面はあるかもしれませんが)

contenteditableのDOMビューを確認したライブラリ一覧

一部のライブラリではEditContext APIが話題に上がっているものもあるため、今後の課題に応じて内部実装が置換されていく可能性はありそうです。
https://discuss.prosemirror.net/t/the-future-of-nytimes-react-prosemirror/5855/12
https://github.com/ianstormtaylor/slate/pull/4177#issuecomment-1997448870

参考資料

https://github.com/w3c/editing/blob/d4a128b77c15fb4e0f3fae1a81a87c61d1d06ffd/docs/EditContext/explainer.md#real-world-examples-of-text-input-issues-in-top-sites-and-frameworks

サイボウズ フロントエンド

Discussion