ボクココ

個人開発に関するテックブログ

拡張テキストエリアを ContentEditable で実現しよう

ども、@kimihom です。

今回は ContentEditable という知る人ぞ知る HTML5 で導入された HTML 属性についてご紹介する。

まず始めに、 大いなる力には大いなる責任が伴うということを伝えておこう。ContentEditable を本気でやろうとすると確実にどハマりし、長い開発期間を要することになる。そして本当にイメージする機能を実現できるかの保証はできない。しかし、その険しい道の行く末に、誰も経験したことのないような、拡張テキストエリアの UX を実現することができる!

ContentEditable で実装できること

まず ContentEditable の簡単な概要をご紹介する。最も一般的に ContentEditable が利用されているのは、おそらく Twitter だろう。公式 Web サイト上のテキストエリアに # の文字を入力してタグ付けをしてみよう。

f:id:cevid_cpp:20171002184710p:plain

ご覧の通り、テキストエリアなのにまず色付けが行われている。そもそも普通の <textarea> ではこれすらも実現不可能だ。また、 # を押した後にはタグの候補が表示されたり、対象のメンションをマウスオーバーするとユーザー情報が表示されたりする。

マークダウンとはなんだったのか。そんなふうに思ってしまうような拡張テキストエリアを、ContentEditable なら実現できるのである。

そして、ContentEditable の原理としては至ってシンプルだ。

<div contentEditable='true'></div>

なんとこれだけで編集可能なテキストエリアを実現できる。Twitter のテキストエリアの部分のHTMLソースを見てもらえばわかる通り、<textarea>ですらないのがポイントだ。CSS や JavaScript を駆使して、 Twitter のツイートエリアのような拡張テキストエリアを作り上げるのである。

ContentEditable を始めてみる

ContentEditable にワクワクした読者の皆様、ようこそ茨の道へ。これの実現には本当にたくさんの罠をくぐり抜ける必要がある。

そもそも、ContentEditable は よくあるブログの編集画面のような、ツールボタンをクリックして文字を太字にしたり色付けしたり画像を挿入したりといった使われ方を想定した設計になっている。これであれば、ContentEditable は容易に実現できる。

f:id:cevid_cpp:20171002185433p:plain

具体的には、 document.execCommand を呼び出すことで書式変更をする。つまり、書式変更ボタンをクリックしたら、document.execCommand('bold') といったようなコマンドを実行するだけで、書式変更のかかった文字を ContentEditable 内で入力させることができるようになる。

しかし、今回私たちが最終的に実現したいのは、Twitter のようなリアルタイムで変わるクールなインタフェースだ。これの実現は半端なく難しいので、チャレンジできる人だけ来て欲しい。

まず、 Twitter ライクな実装を実現するためには、個々のキー入力イベント "keydown" , "keyup" を補足し、その文字によって対応する処理を変えていく必要がある。「"スペース+@"の文字が入力されたら、候補となるユーザー一覧をテキストエリア下部にリスト表示する」 といった実装となる。ご想像の通り、あらゆる文字入力が考えられる中で、その全てのキー入力に対応して違和感を起こさないような実装が必要となってくる。単なる文字だけでなく、Enter, Space, 上下左右、Shift, Control とった文字まで、keydown, keyup イベントは全てを補足するので細かな実装とテストが必要だ。 一応記しておくと、keydownは文字入力される前に呼ばれるイベントで、keyup は文字入力された後に呼ばれるイベントだ。ContentEditable ではこのイベントの使い分けも非常に重要になる。

最終的に生成される文字列は、当然 HTML だ。改行なども<div>タグで表現されたり、書式は <span> で登録されたりする。そのためちゃんとしたテキストとしてデータベースに保存したいのなら、そのHTMLを解析して最終的に整形した文字列を保存しなければならない。そのまま保存して表示みたいなことをすれば、簡単にXSSの脆弱性を生み出してしまうことだろう。

また、キー入力イベントのハンドリングだけではなく、ContentEditable 内のカーソル移動も検討する余地がある。例えば自動入力でテキストエリアに文字を挿入した場合、カーソルはそのテキストエリアの前や後ろに持っていきたいなど、機能の特性によってカーソル位置を変えたいことが出てくる。そこで登場するのが、Range という概念。これも ContentEditable の実装をすると確実に必要になって来るのでドキュメントを見て学んでいただきたい。また、ExecCommand の一部が正しく動作しないとか普通に起こるので、替わりに Range を使って代用する方法など、数多くの地雷が待ち受けていることだろう。

やがてあらゆるハードルを乗り越えれば、リアルタイムに編集しながら自由に HTML を作り上げることができるという夢のような機能を実現できる。

そこまで苦労して ContentEditable を実現したいか。この点に関してはきっとサービス特性によって異なることだろう。

終わりに

私は ContentEditable の持つ魔の力を利用して、拡張テキストエリアを実現することができた。Twitter のタグを見ればわかる通り、普通ならタグ入力の別の検索ボックスのようなものを用意しなければならなかったのが、1つのテキストエリアで全てが表現できるようになった。ContentEditable はそこに価値を感じるか感じないかの世界だ。シンプルで柔軟性のあるサービスを本気で目指しているのなら、ContentEditable の実装を検討してみてはいかがだろうか。

大いなる力には大いなる責任が伴う。ContentEditable で拡張テキストエリアを実現するかしないかを検討してみていただきたい。

続編書きました。

www.bokukoko.info