リッチテキストエディター(RTE)のJSライブラリ色々試してみた
リッチテキストエディタ(RTE)って?
リッチテキストエディタ(以下RTE)とは、文字を入力できるだけでなく、文字に装飾を加えたり、段落を設定できたりと複雑な機能を持つエディタのことです。
弊社プロダクトであるkintone内にも以下のようなエディタが存在します。
また、似たものを指すWYSIWYG(読み方:ウィジウィグ)という用語もありますが、これはWhat You See Is What You Get(見たままが得られる)の略であり、編集時と出力時の見た目が同じエディタのことを指します。
Zennやesaのように、Markdown記法で編集したものが変換されて表示されるようなエディタは含みません。
このようにWYSIWYGはRTEより狭義の意味になっています。
WYSIWYGエディタ(Google Docs)
WYSIWYGではないエディタ(esa)
独自データモデル VS DOMツリーデータモデル
後ほど具体的なライブラリについては見ていきますが、最近のRTEライブラリはDOMツリーデータモデルではなく独自のデータモデルを採用しているケースが多いです。
なぜDOMを直接扱わないかについて、以下で詳細に見ていきます。
異なるDOMでもRTEとしては同じ表示になるケースがある
例えば、下記のようなDOMがあるとします。
<i><b>LoremIpsum</b></i>
<i><b>Lorem<b><b>Ipsum</b></i>
<b><i>LoremIpsum</i></b>
上記のHTMLはi
タグ(フォントをイタリックにする)とb
タグ(フォントを太字にする)の様々な組み合わせで表現されていますが、見た目はいずれも下記のようになります。
RTEとしての表示は上記三行に違いはありません。
このようなデータをRTEで扱うと無駄な変更履歴が発生したり、アルゴリズムが複雑になりパフォーマンスが落ちてしまうケースがあります。
カーソル位置や選択位置を計算しにくい
カーソル位置や選択位置を表現、編集する場合、ツリー構造だと複雑になってしまいます。
下記の図はProseMirrorのGuideから抜粋しています。
上記のDOM構造を、ProseMirroでは下図のような独自データで表現しています。
このようにデータモデルをフラットにし、スタイルをメタデータとして保持することでカーソル位置や選択位置を文字のオフセットで表現することができ、データの分割やスタイルの変更などをツリー操作なしに行うことができます。
また基本的にどのライブラリもHTML構造から制限を加える形で独自データを表現しているため、スタイルの統一やセキュリティ対策、情報の簡易化によるパフォーマンス改善などが期待できます。
独自データ構造を採用する欠点として、HTMLを直接編集するエディタを提供することができません。
HTMLエディタを求めている場合は要注意です。
リッチテキストエディタのJSライブラリ
それでは、具体的なライブラリについて詳しく見ていきましょう。
(以下に記載しているライブラリ情報は執筆時点のものになりますのでご注意ください)
Draft.js
Starts | Version | Updated | Created |
---|---|---|---|
22,557 | 0.11.7 | 4 years ago | 8 years ago |
Meta社製ということで信頼が高いライブラリですが、現在はメンテナンスモードとなっており、機能アップデートは行われない予定となっています。ライブラリ側では後続のLexicalへの移行を推奨しています。
今後Draft.jsからLexicalへの詳細な移行ガイドが追加されるかもしれません。
For users looking for an open source alternative, Meta have been working on migrating to a new framework, called Lexical. It's still experimental, and we're working on adding migration guides, but, we believe, it provides a more performant and accessible alternative.(GitHub Readmeより抜粋)
オープンソースの代替を探しているユーザーのために、MetaはLexicalと呼ばれる新しいフレームワークへの移行に取り組んできました。これはまだ実験的なもので、移行ガイドの追加に取り組んでいるところですが、より高性能でアクセスしやすい代替案を提供できると信じています。(DeepL翻訳)
Lexical
Name | Starts | Version | Updated | Created |
---|---|---|---|---|
lexical | 18,545 | 0.16.1 | a month ago | 10 years ago |
こちらは先ほどのDraft.jsの後継に当たるMeta社製のライブラリです。
まだバージョンがv1以下なのでプロダクト採用する際は注意が必要です。
TS対応/React対応されており、スタイルを持たないシンプルな構成になっています。
WCAG対応などのアクセシビリティも考慮されているとのことです。
また、独自データ構造を採用しています。
ここで紹介する他のライブラリより比較的最近公開されたため[1]、シェアや事例についてはまだまだですがモダンさやMeta社製という信頼性の高さからこれから普及していくのではないかと思っています。
Swift版がサポートされているのも特徴です。
ちなみにAndroidのネイティブサポートやReactNativeサポートについては現在は行われていません。
I’m in the very early days of looking in to what a React Native wrapper around Lexical iOS would look like.
...
(I know I’ve not mentioned Android either. We have nothing to talk about there yet, alas.)
https://github.com/facebook/lexical/discussions/2410#discussioncomment-2946201
私は、Lexical iOSのReact Nativeラッパーがどのようなものになるかを検討しているごく初期の段階だ。
...
(Androidについても触れていないのは承知している。 まだ何も話すことがないんだ、残念だけど)
Slate
Name | Starts | Version | Updated | Created |
---|---|---|---|---|
slate | 29,404 | 0.103.0 | 3 month ago | 12 years ago |
ここで紹介するどのライブラリよりも非常に低レイヤーな構成のライブラリといった印象です。
コアコンセプトに記載されている「first-class plugins」や「Schema-less core」といった項目からもそれが伝わるかと思います。
React/TS対応されており、そのプリミティブな構成から、独自データ構造に制約が少なくカスタマイズが非常に柔軟です。
また、共同編集の実現をベースとして設計されていて、内部的にはYjsが使用されています。
注意点として現在ベータ版であり、また作者であるianstormtaylorさんはここ数年コミットしていないようです。
リポジトリのInsightを見る限りソース変更は減少傾向にあり、不安要素となっています。
ProseMirror
Name | Starts | Version | Updated | Created |
---|---|---|---|---|
prosemirror-view | 1,603 | 1.33.9 | 13 days ago | 8 years ago |
※ProseMirrorはモジュールが細かく分かれているため、上記以外にもパッケージがたくさんあります。
ここで紹介するライブラリの中で、Slateに次いで2番目にプリミティブな構成だと感じました。
ProseMirrorを使用した派生ライブラリが多く(TipTap,ReMirror等)、PriseMirror側もそういった利用方法を推奨しており、RTEライブラリを構築するキットとしての立ち位置を目指しているようです。
The core library is not an easy drop-in component—we are prioritizing modularity and customizability over simplicity, with the hope that, in the future, people will distribute drop-in editors based on ProseMirror. As such, this is more of a Lego set than a Matchbox car.
将来、ProsseMirrorをベースにしたドロップインエディタを配布してくれることを期待して、シンプルさよりもモジュール性とカスタマイズ性を優先しています。 そのため、これはマッチ箱の車というよりは、レゴのセットのようなものです。 (Deepl翻訳)
参考:https://prosemirror.net/docs/guide/
ProseMirror自体はライブラリに依存しない実装になっているため、Reactの直接サポートは行われていません。Reactで利用する場合はReact対応の派生ライブラリであるTipTap,ReMirrorを利用するか、これらの実装を参考にすると良いでしょう。
独自データ構造を採用しており、データ更新はimmutableとなっているのも特徴です。
Quill
Name | Starts | Version | Updated | Created |
---|---|---|---|---|
quill | - | 2.0.2 | 3 month ago | 12 years ago |
QuillはSlab社が開発している、ダウンロード数が安定して多いライブラリです。
長いことアップデートがなく心配でしたが、つい最近v2にアップデートされました。
API駆動設計となっているのが特徴で、RTE内のコンテンツを扱うための豊富なAPIが公開されています。
また、フレームワークに依存しない作りとなっているため、各フレームワークに組み込む際は注意が必要です。
独自データ構造を採用しています。
CKEditor
Name | Starts | Version | Updated | Created |
---|---|---|---|---|
CKEditor | 8,800 | 42.0.2 | 7 days ago | 7 years ago |
古くからある有名なライブラリです。使用したことがある方も多いのではないでしょうか?
CKEditor4->5へのアップデートはライセンスを含め破壊的に変更されています。
最新のCKEditor5はオープンソース版では GPL 2+ライセンスでありコピーレフトなので注意が必要です。
有料版では一般的な商用利用が可能ですが、詳しい料金体系はサイト上に情報がなく、問い合わせが必要となっています。
またCKEditorはHTML編集が可能となっており、独自データを描画に採用していないことがわかります。
demo:https://ckeditor.com/ckeditor-5/demo/html-support/
TinyMCE
name | Starts | Version | Updated | Created |
---|---|---|---|---|
TinyMCE | 14,710 | 7.2.1 | a month ago | 10 years ago |
CKEditorと同じく古くからある有名なライブラリです。
また、こちらもアップデートによってライセンスが変更されており、オープンソース版のTinyMCE7のライセンスはGPLv2 or laterとなっています。
一般的な商用利用向けのクラウド版(有料)も用意されています。詳しい料金体系については公式の案内をご確認ください。
自作プラグイン実装は下記の二つの実装方法があります。
-
external_plugins
オプションを用いてURLでプラグインの場所を指定する - pluginsフォルダーに格納し、
plugins
オプションから呼び出す
公式はexternal_plugins
を推奨しており、他のライブラリに比べて使い勝手に癖があるように感じました。
現状データ構造はDOMで管理されており、HTMLエディタとして使用することが可能です。
ただしデータモデルのオプションのページが設けられており、将来的に独自データ構造も採用可能になるかもしれません。
モバイル版もサポートされており、スクリーンリーダー互換があります。
まとめ
現状ではこのライブラリが一番!というものはなく、それぞれに長所・短所があり、プロダクトによって最適なライブラリが変わるように思いました。選択に悩んだ際は、以下のポイントに注目してライブラリを選定するのがおすすめです。
- HTML編集機能を実装する予定があるか
- カスタマイズ製をどこまで求めるか
- ネイティブ or Webビューでのモバイルサポートが必要か
- 使用したいフレームワーク(React等)での利用が難しくないか
- 共同編集機能を実装する予定があるか
- プラグイン開発が必要か/必要ならば開発は容易か
- アクセシビリティ対応が求める水準に達しているか
- その他ライブラリで共通の比較事項(スター数・コミュニティ・更新頻度・コントリビュートの活発さなど)
RTEの導入にあたってこの記事が参考になれば幸いです🏋️♀️
-
npmのcreatedは10年以上前ですが、GitHub上でソースが公開されたのは2022年4月頃でした。https://jser.info/2022/04/18/lexical-react-testing-library-v13.1.0-dialog-component/ ↩︎
Discussion