textareaを使った疑似リッチテキストエディタ
フルスタックの Javascript 製テキストエディタには VSCode で採用されている Monaco Editor や先駆のひとつといえる Ace, Github の開発した Atom といった優れた実装があります。
これらの隙間を縫って、より広範なブラウザに対してそこそこの編集環境をグレイスフルデグラデーションしつつ提供する、という方針でエディタの開発について検討してみます。
さて、DOM がなんだかんだいいつつその初期から安定していたのに比してテキスト編集関連の API である <textarea>
, [contentEditable="true"]
, iframe.contentDocument.designMode="on"
の実装状況は様々であり、その上必ずしも最も古い API の実装が最も充実しているわけでも無いようです。
この記事シリーズでは各 API について愚直に実装しテストすることで、ふんわりと語るしかなかった実装状況をなるべく明らかにしていきたいです。
最初の記事では最も古くからある <textarea>
を使ってエディタを実装しました。この実装では <textarea>
の制約から要素の編集中は HTML ソースが表示されます。この為、疑似 WYSIWYG エディタとしています。
クロスブラウザ対応の戦略 | テキスト編集能力 | |
---|---|---|
フルスタックエディタ | 低レベルな API を駆使してフルスクラッチしているので、ブラウザの差異にあまり遭遇しないと思われる | 数万行の文書の編集でもパフォーマンスを落とさないのは、DOM の再利用やワーカーを駆使しているから、と思われる。 |
そこそこのエディタ | ビルトインの編集 API の実装状況のリストを持って、基本的にそれを使う。時には一部機能を諦める。 | ビルトインの編集 API に頼った実装になるので、ネイティブのスピードが出たり、その限界に拘束されたりする。小~中規模の文書をそこそこ扱えれば良しとする。 |
実装済
編集開始
- クリックした要素が
DDTD
に一致する場合、編集エリア化する。 - この際のクリック位置が、編集エリアのカーソル位置になる。
- この際に選択していたら、編集エリアに反映される。
- 要素を跨いで選択した場合、ポインターアップ時の要素が編集エリア化する。選択範囲は編集中要素に切り詰められる。
編集エリアの移動
- 編集エリアの最初にカーソルがある状態で ← を押すと、直前の
DDTD
に一致する要素に編集を移る。カーソルは行末。 - 逆。カーソルは先頭。
- 上下キーによる編集エリアの移動。(要素の行数と各行に存在する文字数から意図した移動が可能)
編集
- 改行文字の入力で
<P>
タグの分割、複数の改行を持つテキストのペーストにも対応 - 行頭で BS キーで前の要素と結合、行末で DEL キーで後の要素と結合
- 空文字の状態でカーソルを失った際に、その要素は削除する
気づき
- IME 変換中のキーイベント(方向キー)は発生しない?
- Windows 10 の Firefox と Chrome はカーソルが行末にある場合と次の行頭にある場合で
offset
値が同じ… そもそも IE は途中の行の行末にはカーソルがいかない - IE は1行目でカーソル上を押したときカーソルがホーム位置に行かない、最終行でカーソル下を押した場合にカーソルがエンド位置に行かない
- IE8 以下では
textarea
,input[type=text]
にフォーカスする際にタイマーを挟む必要がある - IE では9以上でサポートした
selection
を使うとクリックで編集をはじめた際に1文字が選択される - IE では rect の補正が必要。その補正法は IE11 の IE8 モードと IE7 モードでも差異がある。
.select()
出来ない問題、iOS, Android の Firefox Chrome ではclick
イベント内でfocus()
を与える。
未実装
- キー入力時に編集エリアのカーソル位置が画面外なら、スクロールする。但し Firefox 58 はブラウザ側でスクロールした。
<textarea>
内のカーソル位置からピクセル座標を取得するには隠し要素による測定が必要。 - 初期の Chromium で
<textarea>
に対して CSS が無視される。(Iron 5 で確認。)width
でなくcols
等を使う。