はじめに
「リッチテキストエディタを導入したいけれど、あの独特な設定やAPIを覚えるのが面倒」——そんな経験はないでしょうか。TinyMCEやCKEditorは高機能な反面、設定項目が多く、出力されるHTMLも独自のクラスや属性まみれになりがちです。
Trixは、Basecamp(旧37signals)が自社サービスのBasecampやメールサービスHEYのために開発した、シンプルさに振り切ったWYSIWYGリッチテキストエディタです。<trix-editor>というカスタム要素をHTMLに置くだけで、太字・見出し・リストといった基本的な書式編集がすぐに使えるようになります。設定ファイルもビルドステップも不要で、出力されるのは余計なマークアップの少ないクリーンなHTMLです。
とはいえ、説明より実際に触った方が早いはずです。ブラウザ上でそのまま動かせるTrixのサンプルを用意したので、先に挙動を見たい方はこちらからどうぞ。
Trixとは
TrixはBasecampが開発したオープンソースのリッチテキストエディタです。ブラウザのcontenteditableを土台にしつつ、編集操作を一度すべてTrix独自のドキュメントモデルに変換し、そこからHTMLを再描画するという設計を取っています。これにより、ブラウザごとのcontenteditable実装の差異に振り回されることなく、一貫した編集体験を実現しています。
GitHubで19,000スター以上を獲得しており、Basecamp本体やHEYで実際に使われています。Ruby on RailsにはAction TextというTrixをラップした標準機能もあり、Rails開発者にはおなじみの存在かもしれません。とはいえRails専用というわけではなく、<trix-editor>はただのカスタム要素なので、React・Vue・素のJavaScriptなどフレームワークを問わず組み込めます。
主な特徴
- カスタム要素だけで導入完了 -
<trix-editor>タグを置くだけでツールバー付きのエディタが動く。設定ファイルは不要 - contenteditableの差異を吸収 - 独自のドキュメントモデルを内部に持ち、ブラウザ間の挙動差を吸収してくれる
- クリーンなHTML出力 - 余計な
<span>や属性を生成しない、整形済みのシンプルなHTML - ファイル添付をネイティブサポート - 画像やファイルをドラッグ&ドロップするだけで添付でき、
trix-attachment-addイベントでアップロード処理を差し込める - hidden inputとの自動同期 - フォーム送信用の値を自前で管理しなくても、編集内容が自動的にhidden inputへ反映される
インストール
npmでインストールする場合は次のコマンドを実行します。
npm install trix
// JSとCSSをそれぞれ読み込む
import "trix"
import "trix/dist/trix.css"
CDN経由で読み込む場合は、次のようにscriptタグとCSSを読み込むだけで使えます。
<script type="module" src="https://cdn.jsdelivr.net/npm/trix@2.1.19/dist/trix.umd.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/trix@2.1.19/dist/trix.css">
Trixのサンプルを動かす
下のエディタは<trix-editor>をそのまま置いただけの最小構成です。input属性でhidden inputのidを指定しておくと、編集内容が自動的にそのinputのvalueへHTMLとして同期されます。この仕組みのおかげで、Trixにはフォーム連携用の特別なコードがほとんど要りません。
要点だけを抜き出すと、次のようになります。
<input id="content" type="hidden" name="content">
<trix-editor input="content"></trix-editor>
document.addEventListener("trix-change", (event) => {
const input = document.getElementById("content")
console.log(input.value) // 編集中のHTMLがそのまま入っている
})
実際に動かせるものが下のサンプルです。エディタ内の文章を書き換えたり、ツールバーで太字・見出しを適用したりすると、下の「hidden inputに同期されているHTML」がリアルタイムに変わるのを確認できます。
trix-changeイベントは編集内容が変わるたびに発火し、trix-initializeは<trix-editor>が使える状態になったタイミングで発火します。この2つを組み合わせれば、初期表示から編集後まで一貫してエディタの状態を追いかけられます。
基本的な使い方
実際のフォームに組み込む場合は、hidden inputと<trix-editor>をペアで置くだけです。
<form action="/messages" method="post">
<input id="message_content" type="hidden" name="message[content]">
<trix-editor input="message_content"></trix-editor>
<button type="submit">送信</button>
</form>
import "trix"
import "trix/dist/trix.css"
フォームを送信すると、message[content]パラメータには編集中に整形されたHTMLがそのまま入って送られます。サーバー側で特別なパース処理を書く必要はなく、通常のフォームパラメータとして受け取れます。保存前にサニタイズしたい場合は、RailsのAction Textのように、サーバー側で許可タグをホワイトリスト方式にフィルタするのが定石です。
実践的なユースケース
文字数カウントと上限警告
SNS投稿欄やコメントフォームのように、入力できる文字数に上限があるケースです。trix-changeイベント発火時にeditor.getDocument().toString()でプレーンテキストを取り出し、その長さを数えることで文字数カウンターが作れます。上限を超えたら投稿ボタンを無効化する、という実装が典型です。
document.addEventListener("trix-change", (event) => {
const text = event.target.editor.getDocument().toString().trimEnd()
const length = text.length
const over = length > 60
// カウンター表示や送信ボタンのdisabled切り替えに使う
})
下のサンプルは60字を上限にしたコメント欄です。文字数がカウンターに反映され、超過すると赤字になり投稿ボタンが押せなくなります。文章を打ったり削ったりして、しきい値をまたぐ瞬間の挙動を確認してみてください。
LIMITの値を変えると、投稿ボタンが押せなくなるタイミングが変わります。getDocument().toString()はHTMLタグを含まないプレーンテキストを返すので、太字や見出しにしても文字数のカウントには影響しません。
ファイル添付とプレビュー(trix-attachment-add)
Trixは画像をドラッグ&ドロップまたは貼り付けするだけで添付できますが、添付されたファイルは「保存先のURLが決まるまで」pending状態のままです。実運用ではtrix-attachment-addイベントを監視し、XMLHttpRequestでファイルをアップロードして、完了後にattachmentのURLを設定します。
ここではサーバーを使わず、ブラウザ内だけで完結するURL.createObjectURLでアップロード完了を模擬します。実際のアプリではこの部分をXHRでのアップロード処理に置き換えてください。
document.addEventListener("trix-attachment-add", (event) => {
const { attachment } = event
if (attachment.file) {
// 本来はここでXHRアップロードし、完了後にurlを設定する
const url = URL.createObjectURL(attachment.file)
attachment.setAttributes({ url, href: url })
}
})
下のサンプルは「画像を添付」ボタンから画像ファイルを選ぶと、editor.insertFile()でエディタに挿入され、プレビュー付きの添付として表示されるものです。
ボタンを使わず、画像ファイルを直接エディタにドラッグ&ドロップしても同じように動きます。Trixが自動でファイルを検知してtrix-attachment-addを発火してくれるので、アプリ側で書くのは「アップロードしてURLを設定する」処理だけで済みます。
ツールバーのカスタマイズ
コメント欄のような用途では、見出しや引用、コードブロックといったボタンは不要なことがあります。Trixのツールバーはtoolbar属性で参照先の<trix-toolbar>を指定できるため、必要なボタンだけを手書きしたツールバーに差し替えられます。各ボタンの機能はdata-trix-attributeやdata-trix-actionといったdata属性で決まります。
<trix-toolbar id="comment-toolbar">
<div class="trix-button-row">
<span class="trix-button-group" data-trix-button-group="text-tools">
<button type="button" class="trix-button" data-trix-attribute="bold" data-trix-key="b">太字</button>
<button type="button" class="trix-button" data-trix-attribute="italic" data-trix-key="i">斜体</button>
<button type="button" class="trix-button" data-trix-attribute="href" data-trix-action="link" data-trix-key="k">リンク</button>
</span>
</div>
</trix-toolbar>
<trix-editor toolbar="comment-toolbar" input="content"></trix-editor>
下のサンプルは、太字・斜体・リンクの3つだけに絞ったツールバーです。標準のツールバーにある見出しや引用、リスト、ファイル添付ボタンがない、シンプルな見た目になっているのが分かります。
data-trix-button-groupごとにボタンをまとめておくと、CSSでdisplay: noneにするだけでも見た目を絞り込めます。用途に応じて「フル機能のエディタ」と「最小限のコメント欄」を作り分けられるのは、ツールバーがただのHTMLとして書き換えられるTrixならではの手軽さです。
まとめ
Trixを触ってみて感じたポイントをまとめます。
- 導入が驚くほど簡単 -
<trix-editor>を置くだけで、ツールバー付きのエディタがすぐ動く - 出力HTMLがクリーン - 余計なマークアップが少なく、そのまま保存・表示に回せる
- フォーム連携が標準機能 - hidden inputとの同期を自前で書く必要がない
- ファイル添付が組み込み済み -
trix-attachment-addイベントを拾うだけでアップロード処理を差し込める - ツールバーはただのHTML - 用途に応じてボタン構成を柔軟に変えられる
高機能さで殴るタイプのエディタではありませんが、「フォームにちょっとしたリッチテキスト入力を足したい」という場面には過不足のない選択肢です。コメント欄やメッセージ入力欄のように、複雑な文書構造までは要らないけれど太字やリンクくらいは使いたい、という場面でぜひ試してみてください。