Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
RTE ライブラリの仕組みとそれを支えるブラウザ機能
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
kosakin
June 21, 2026
790
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
Web で実用的な縦書きエディタは可能か?──現在地と普及に向けて
karintou8710
0
130
pタグを跨いだテキストコピーは 改行が2つになる!?
karintou8710
0
47
WYSIWYG ルビ入力の概要と Chromium のバグを修正した話
karintou8710
0
320
Sample
karintou8710
0
320
Featured
See All Featured
Darren the Foodie - Storyboard
khoart
PRO
3
3.9k
[SF Ruby Conf 2025] Rails X
palkan
2
1.4k
Practical Orchestrator
shlominoach
191
12k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.3k
Thoughts on Productivity
jonyablonski
76
5.4k
What's in a price? How to price your products and services
michaelherold
247
13k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
520
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
520
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
The agentic SEO stack - context over prompts
schlessera
0
910
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
870
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
930
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