Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
RTE ライブラリの仕組みとそれを支えるブラウザ機能
Search
kosakin
June 21, 2026
650
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
RTE ライブラリの仕組みとそれを支えるブラウザ機能
#rich_text_editor_study
https://web-study.connpass.com/event/391357/
kosakin
June 21, 2026
More Decks by kosakin
See All by kosakin
pタグを跨いだテキストコピーは 改行が2つになる!?
karintou8710
0
39
WYSIWYG ルビ入力の概要と Chromium のバグを修正した話
karintou8710
0
270
Sample
karintou8710
0
310
Featured
See All Featured
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.1k
How to Ace a Technical Interview
jacobian
281
24k
GraphQLとの向き合い方2022年版
quramy
50
15k
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
1.4k
A Modern Web Designer's Workflow
chriscoyier
698
190k
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
64
56k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
250
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.3k
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.7k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
Leo the Paperboy
mayatellez
8
2.1k
Transcript
RTE ライブラリの仕組みと それを支えるブラウザ機能 2026.05.26 (火) #rich_text_editor_study 1
所属:サイボウズ株式会社 (26卒) 出身:札幌 最近の興味:家系・ポーカー 悩み 気合いを入れた名刺入れを購入したが 渡す機会がない 2 自己紹介 コサキン
@karintou74073
自己紹介 (RTE) • インターンや内定者バイトで RTE に関わる • 移行先ライブラリの技術選定と PoC 作成に関わる
• Zenn のリッチテキストエディタを非公式で開発 • Tiptap や Chromium(Blink)に小さくコントリビュート • 最近は Web 縦書きエディタを作ろうと戦っている 3
RTE の理解はまだまだ足りない 5 アクセシビリティ / 共同編集 / AI 活用 /
ブラウザごとの仕様 / RTE ライブラリの内部実装 / 縦書き / RTL / Web 標準 / 理 想の RTE モデル / ブラウザの共通テスト / EditContext の活 用方法 / パフォーマンス / Mobile RTE / Native App RTE / RTE のテスト設計 ...
以下の雰囲気を理解して、関連技術に触れるときに思い出せるように この発表の目標 6 • RTE ライブラリがどのブラウザ機能を使い、何を補っているのか • Web の RTE
開発の概要 • RTE で独自機能を作る上で必要な最低限の知識 基本 探求
Web RTE 概要 7
シンプルなテキスト編集に加えて、文字の装飾や画像などリッチな 入力が可能になったエディタのこと リッチテキストエディタ(RTE)はなにもの? 8 エディタの種類は主に以下がある ・プレーンテキストエディタ ・コードエディタ ・WYSIWYG エディタ (
RTE? ) 用途や実装方法が色々あり、定義が難しい!!
©️ Cybozu, Inc. 実例を見てみよう 9
©️ Cybozu, Inc. 10 <textarea /> <input /> プレーンテキストエディタ •
テキストデータを扱う • 高度なハイライトができない
©️ Cybozu, Inc. 11 VSCode (Monaco) Zenn (CodeMirror) コードエディタ •
テキストデータを扱う • テキストを元に装飾が可能
©️ Cybozu, Inc. 12 Slack (Quill) WYSIWYG エディタ • テキスト・装飾・構造などをデータにもつ
• ユーザーが任意の場所にテキスト装飾・ノード追加が可能 Closure Library
©️ Cybozu, Inc. 13 Notion(独自) Note (ProseMirror) WYSIWYG エディタ
意外と身近に RTE が溢れている 14
開発者目線の視点では? 15
基本的にライブラリを使うのが主流 • 最初から見た目も整っているエディタ:Quill, Mantine, CKEditor, TinyMCE • コア機能だけ提供して、見た目を柔軟にできるヘッドレスエディタ:Tiptap・Lexical RTE が出力する形式(HTML,
JSON)を決めて、表示側で XSS などのセキュリティ対策をするだけ サービスに WYSIWYG エディタを導入したい!となったら? 16
©️ Cybozu, Inc. 17 Quill のシンプルなコード例
• RTE ライブラリの力を借りることで導入はだいぶ簡単! • 用意されたものを使うだけなら仕組みを理解しなくてもいいが、機能拡張は RTE ライブラリの構成要素 を把握する必要がある • メンション機能・テーブルの編集操作用
UI ・差分コードブロック・サービス固有のノード サービスに RTE 導入することは簡単? 18
• RTE とはシンプルなテキスト編集に加えて、文字の装飾や画像などリッチな入力が可能になったエディ タのこと • 用意された機能を使うだけなら仕組みを理解しなくてもいいが、機能拡張は RTE ライブラリの構成要素 を把握する必要がある 振り返り
19
RTE ライブラリで知っておくべき仕組み 20 機能拡張をするためには最低限何から理解すればいい?
ヘッドレスエディタの ProseMirror を参考に説明します。 他のエディタも細かいとこは違えど、似たイメージ(のはず。。。) RTE ライブラリの基本原則 21 • 表示で DOM
Event が発火し、編集操作の Transaction に変換 • Transaction によって状態が更新 • 表示に反映する 表示 状態 操作 ブラウザとRTEの橋渡し ProseMirror の基本フロー
まずは状態 (EditorState) を知る 22
• ドキュメントはリッチテキストを表現するデータのこと • 骨格でツリー構造の Node と、テキスト装飾のフラット構造な Mark • Node には
block と inline の2種類のタイプがある • HTML よりも編集で意味ある単位として定義することが可能 ドキュメントとは何か? 23 相互変換 ドキュメント HTML
• 開発者がドキュメントのスキーマを定義し、意図しない構造になることを防ぐ機能 • ドキュメントのセマンティックを守る / 統一的なスタイル / ブラウザ未対応の機能を使わない • 複雑なHTML構造で意図しない挙動を防げる
• 最近は特定の構造を守りたいと思うことが大半のイメージ ドキュメントを守るスキーマ 24 Node を追加するときに定義するスキーマ • Node の一意な名前 • Node のグループ(block / inline) • 子 Node の(名前 / グループ)・個数・順番
スキーマ例 25 スキーマ 正しい 不正な構造 ルートの下に一つ以上の <p>, テキストノードを子に持つ
©️ Cybozu, Inc. Tiptap (ProseMirror) の実装例を見る 26
Tiptap で最も基礎的なコード 27 ・Tiptap は Node 単位で Extension を作る ・Document
... ルートノード(特殊) ・Paragraph ... p ノード ・Text ... テキストノード(特殊)
name, group, content がスキーマ部分 ドキュメントとHTMLを相互変換 • <p> → paragraph Node
に parse • paragraph Node → <p> に変換 • 0 は子ノードの renderHTML に委任 • 今回だとテキストノードをレンダリング Paragraph Node 28
• 意味単位で DOM を扱いたい • 編集操作では DOM が持つ編集文脈を考慮する必要がある • 「今の選択範囲はコードブロックの中にあるので、Enter
は改行コードを入れる」など • テキスト装飾で顕著 • 下画像の縦棒の位置で Enter を押す → ブロックレベルで分離したい • DOM だと親の親を見ないと、段落の中にいることを認識できない • ドキュメントだと <b> はマークなので、 paragraph block を分割すればいい(次ページ) なぜ DOM を直接扱わないのか?ドキュメントに抽象化する意味は? 30
©️ Cybozu, Inc. 31 ドキュメントの例
• 今どの位置を選択しているのかを示す • Web API では DOM で表現するが、ProseMirror では扱いやすいように絶対位置の番号が割り当て •
細かいところは割愛 ... 選択範囲(Selection) 32
状態 (EditorState) 33 ・ドキュメント ・選択範囲 ・ドキュメント ・選択範囲 操作 State 1
State 2 ・今いる位置から一文字後方削除 ・今いる位置に 「a」を挿入 ・位置 2 – 4 を削除 ...
状態 (EditorState) がわかった! 34
再掲:RTE ライブラリの基本原則 35 表示 状態 操作 ブラウザとRTEの橋渡し ProseMirror の基本フロー •
表示で DOM Event が発火し、編集操作の Transaction に変換 • 基本的な操作はライブラリが対応 • Transaction によって状態が更新 • 表示に反映する
• 他にも沢山の拡張要素がある • 特定キーを入力した時 • 特定の文字列を入力した時 • 編集画面のみ見た目を一部変えたい(テーブル編集UI) • プレースホルダーをノードの種類ごとに切り替えて表示したい
• ブロック単位で DnD したい • Etc ... 他の拡張要素 36
• 「表示 → イベント → 操作 → 状態更新」 が基本フロー •
最初のとっかかりとして、状態を押さえよう!ドキュメントと選択範囲で構成される • 基本的な操作はライブラリが提供しているので、状態を定義すれば機能拡張可能 振り返り 37 表示 状態 操作 ブラウザとRTEの橋渡し
RTE ライブラリの ブラウザの機能を用いた実現方法 38
ブラウザの機能だけで実現できないの? 39 ・そもそも RTE を0から開発するには何が必要?
• HTML の属性の一つで、その要素の配下を編集可能にする • キャレット表示 / キー入力で状態操作 / 範囲選択 /
編集履歴 ... • 属性が取るべき値は HTML Living Standard に定義されているが、内部の編集ロジックは仕様がない • 例)Enter 押した時に何をする? • Web Editing WG では contenteditable の編集部分の標準化を進めず、 EditContext API に取り組んでいる Web RTE のコア:contenteditable とは 40
表示 • DOMの描画 • 現在選択中のキャレットと選択範囲を矩形で描画 そもそもブラウザがRTEを0から実装するためには? 41 入力 • キー入力(英数字・Enter・Backspace・矢印
...) • クリック / 範囲選択 で Selection を更新する • IME / クリップボード / DnD これらの機能は、contenteditable を ON にするだけで対応できる! 状態 • DOM / Selection(DOMベース) 操作 ・Insert / Delete / Replace / MoveSelection ...
• スキーマ定義ができない • contenteditable は任意の HTML を許可してしまうため、構造化されたデータモデルが作れない • 操作ロジックの拡張が難しい •
ブラウザの編集履歴を管理する API が無く、DOM 操作では履歴が保存されない • execCommand / queryCommandState 等の HTML 操作 API も非推奨 • ブラウザ間で挙動が揃わない • 特殊キー(Enter や Backspace 等)の処理がブラウザごとに異なる • 一つずつ吸収するより、ブラウザに処理をさせない方が楽 じゃあ contenteditable だけでよくね?→ 厳しい 42 中途半端なカスタマイズは困難。 DOM Event でフックし、ブラウザに編集をさせない方向へ
RTEライブラリが必要なのはわかった。 ブラウザとの分担は? 43
担当領域の違い (ProseMirror) 44 入力 キー入力 / Clipboard ... ブラウザ ProseMirror
(JS) ・DOM Event ・DOM の差分反映 ・Selection API Transaction History 管理 状態 ・DOM (HTML) ・DOM Selection 表示 ・HTML/CSS ・キャレット ・選択ハイライト 状態 ・ドキュメント ・選択範囲 レンダリング ・Mutation Observer ・selectionchange handler ビルトインの操作 ・テキスト(IME) ・矢印キー ・Enter などの特殊キー ・NodeSelectionになる場合の移動など ブラウザと ProseMirror で双方向に状態を同期している
©️ Cybozu, Inc. データ反映:ProseMirror → ブラウザ 45
状態反映:ProseMirror → ブラウザ 46 入力 キー入力 / Clipboard ... ブラウザ
ProseMirror (JS) ・DOM Event ・DOM の差分反映 ・Selection API Transaction History 管理 状態 ・DOM (HTML) ・DOM Selection 表示 ・HTML/CSS ・キャレット ・選択ハイライト 状態 ・ドキュメント ・選択範囲 レンダリング handler ブラウザと ProseMirror で双方向に状態を同期している ・Enter などの特殊キー ・preventDefault でブラウザに処理させない フロー図
状態反映:ProseMirror → ブラウザ 47 入力 キー入力 / Clipboard ... ブラウザ
ProseMirror (JS) ・DOM Event ・DOM の差分反映 ・Selection API Transaction History 管理 状態 ・DOM (HTML) ・DOM Selection 表示 ・HTML/CSS ・キャレット ・選択ハイライト 状態 ・ドキュメント ・選択範囲 レンダリング handler ブラウザと ProseMirror で双方向に状態を同期している ・Enter などの特殊キー ・preventDefault でブラウザに処理させない フロー図
©️ Cybozu, Inc. データ反映: ブラウザ → ProseMirror 48
状態反映:ブラウザ → ProseMirror 49 入力 キー入力 / Clipboard ... ブラウザ
ProseMirror (JS) ・DOM Event Transaction History 管理 状態 ・DOM (HTML) ・DOM Selection 表示 ・HTML/CSS ・キャレット ・選択ハイライト 状態 ・ドキュメント ・選択範囲 レンダリング ・Mutation Observer ・selectionchange handler ビルトインの操作 ・テキスト(IME) ・矢印キー 差分を検知 フロー図 handler ・preventDefault しない
状態反映:ブラウザ → ProseMirror 50 入力 キー入力 / Clipboard ... ブラウザ
ProseMirror (JS) ・DOM Event Transaction History 管理 状態 ・DOM (HTML) ・DOM Selection 表示 ・HTML/CSS ・キャレット ・選択ハイライト 状態 ・ドキュメント ・選択範囲 レンダリング ・Mutation Observer ・selectionchange handler ビルトインの操作 ・テキスト(IME) ・矢印キー 差分を検知 フロー図 handler ・preventDefault しない
• 本来であれば ProseMirror → ブラウザの順方向だけにしたい • ブラウザ依存が減る / 状態反映が単方向のみでシンプル •
しかし、現実は技術的にブラウザに頼る場面がいくつか存在する IME 入力 • 途中で DOM を変更すると中断されてしまうため、ブラウザに処理をさせる必要がある 矢印キー • 矢印キーによる上下移動はレイアウト依存のため、自前実装が非常に困難 • 例)width による改行がある段落だと、DOM から次の行を判断できない • bidirectional text の実装は厄介(LTR と RTL が混ざったテキスト) クリック / 範囲選択 なぜ逆方向の状態反映が必要なの? 51
ブラウザだけでは無理 しかし、RTE ライブラリだけでも厳しい 52
• 知的好奇心が大きいかもしれない • ブラウザ editing の強みと辛い箇所を知れる • ライブラリを作る側に回るときに役に立つかも? • 自分は深海まで潜れるぞという安心感がある
ブラウザとRTEの役割分担を知って意味ある? 53
• ブラウザ担当の箇所を全て自前実装する必要が出てくる。厳しい • どこを追加実装必要? • HTML + CSS のレンダリングもどきを自前実装 •
テキスト入力や移動周りのロジック • アクセシビリティ周り(そもそも自前実装可能なのか?) • 現状だと IME を使うには textarea にフォーカスを当てる必要があるため、ハックが必要 (余談) <canvas /> での実装は? 54
• contenteditable 単体では現代の要件を満たせない • RTEライブラリはブラウザに基本任せない方針だが、任せる領域も存在している • DOMやキャレットの描画 • IME 含むキー入力
まとめ 55
©️ Cybozu, Inc. 56