It's an HTML attribute that allows an element to be treated like a directly editable text area within the browser.
HTML要素に contenteditable="true"
を指定すると、その要素内のテキストがブラウザ上で直接編集できるようになります。これは、ユーザーがWebブラウザ上で自由にテキストを変更できるインラインのエディタのような状態を作ることができるということです。
たとえば、下記のような要素をHTMLファイル内に書くと、該当要素をクリックして内容を編集できるようになります。
HTML
<div contenteditable="true">
このテキストはブラウザ上で編集可能です。
</div>
contenteditable
には大きく分けて以下の2種類の値を指定できます。
contenteditable="true"
contenteditable="false"
また、明示的に指定しない場合でも、JavaScriptを用いて動的に値を設定することで編集可能にできます(例:element.contentEditable = "true"
)。
どのような要素であっても、contenteditable
属性を付与すればその要素自体が編集可能領域になります。
<div>
や <section>
などのブロック要素でもOK<span>
や <a>
などのインライン要素でもOKただし、要素の見た目や振る舞いが大きく変わる可能性があるため、どの要素に編集機能を付けるか検討する必要があります。
最もシンプルな利用方法は、以下のように contenteditable="true"
を直接HTML要素に記述することです。
HTML
<div contenteditable="true" style="border: 1px solid #ccc; padding: 10px;">
ここをクリックしてテキストを編集できます。
</div>
上記のように書くだけで、ユーザーは該当の要素をクリックし、テキストを直接編集できます。
ユーザーが編集した内容を取得し、サーバーに送信したり、別の場所に表示したりしたい場合は、JavaScriptを利用して要素の innerHTML
や textContent
を取得できます。
HTML
<div id="editable" contenteditable="true">
編集したいテキスト
</div>
<button id="showResult">編集内容を表示</button>
<pre id="resultArea"></pre>
<script>
const editable = document.getElementById('editable');
const resultArea = document.getElementById('resultArea');
const showResult = document.getElementById('showResult');
showResult.addEventListener('click', () => {
// innerHTMLで取得すると、HTMLタグ込みの文字列が得られる
// textContentで取得すると、テキストのみが得られる
const content = editable.innerHTML;
resultArea.textContent = content;
});
</script>
このようにすれば、編集後の内容を取得して、pre
要素に表示することができます。HTML構造まで含めて取得したい場合は innerHTML
を使い、単純なテキストだけを取得したい場合は textContent
を使うとよいでしょう。
contenteditable
で編集すると、ユーザーがコピー&ペーストをしたり、リッチテキスト編集が有効になっているブラウザを使って入力したりすると、余計なHTMLタグやスタイル属性が混在することがあります。これはブラウザやユーザーの操作によって異なるため、意図しないタグが入り込んでしまう恐れがあります。
対策としては、入力内容をサーバーへ送信する際にHTMLのサニタイズを行う、またはクライアント側でHTMLをクリーニングする仕組みを用意するなどが挙げられます。
ブラウザやOSによって、改行時に自動で <br>
が入る場合や <div>
が入る場合など、挙動が異なることがあります。想定外のマークアップが入り込む場合があるため、取り扱いには注意が必要です。
また、連続する空白はHTMLの仕様上、連続空白がまとめられてしまうケースがあるため、ユーザーが意図したとおりにテキストが整形されないこともあります。
モバイル端末では、ソフトウェアキーボードの表示タイミングや、予測変換による入力支援などの結果が思わぬ形で反映されることがあります。PCと同じ感覚でテキスト編集エリアを提供しても、思い通りに動かないケースがあるため、事前のテストが重要です。
ユーザーが自由にHTMLを入力できるということは、潜在的にXSS(クロスサイトスクリプティング)などのリスクが高まる可能性があります。編集した結果にスクリプトなどが埋め込まれていないか、送信前や表示前に適切に検証・サニタイズする必要があります。
contenteditable
は、昔からある document.execCommand()
API と組み合わせて利用されることが多かった機能です。しかし、execCommand()
は現在非推奨となっており、将来的に廃止される可能性があります。
かつては、execCommand('bold')
のように呼び出すことで、選択中のテキストを太字にするなどの簡易的なリッチテキストエディタを実装できました。今後はブラウザごとに代替のAPIが登場していく可能性がありますが、現時点では完全な代替となる標準APIは整備中の段階です。
近年では、より低レベルなテキストの範囲指定を行うために、Selection API や Range API を使って操作する方法が推奨されています。ユーザーが選択したテキストを取得したり、その範囲を置換したりすることが可能です。
JavaScript
// 選択されているテキスト範囲を取得
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
// Rangeオブジェクトを用いて、選択範囲を囲む要素を作るなどの操作が可能
}
これらのAPIを活用することで、contenteditable
領域内のテキストをより柔軟に操作することができます。
contenteditable
自体にはUndo/Redoを行う機能がブラウザレベルで備わっています。ユーザーはキーボードショートカット(例えばCtrl+Z/Command+Zなど)で元に戻すことができますが、アプリケーション側で細かくUndo/Redoの挙動を制御するのは容易ではありません。
より高度な制御を行いたい場合、操作の履歴を自前で管理する仕組み(例えばSquireやProseMirrorなどのライブラリを利用)を用意して、テキスト編集の状態を監視しながらUndo/Redoを実装することが考えられます。
ここでは、contenteditable
を活用した簡易リッチテキストエディタの例を紹介します。上記で述べたとおり、execCommand()
は将来的に非推奨ですが、シンプルに理解する例としては有効です。
HTML
<div id="toolbar">
<button type="button" onclick="document.execCommand('bold')">太字</button>
<button type="button" onclick="document.execCommand('italic')">斜体</button>
<button type="button" onclick="document.execCommand('underline')">下線</button>
</div>
<div id="editor" contenteditable="true">
ここをクリックして編集開始
</div>
<script>
// シンプルな例につき、特別な処理は割愛
</script>
CSS
#toolbar {
margin-bottom: 10px;
}
#editor {
border: 1px solid #ccc;
min-height: 100px;
padding: 10px;
/* contenteditableによる入力に合わせてスタイルを整える */
}
上記のボタンをクリックしてテキストを選択している状態であれば、太字、斜体、下線の操作が可能です。現時点でのブラウザでは動作しますが、将来的には標準APIとして別のアプローチが推奨される見込みです。
contenteditable
だけでリッチなテキストエディタを実装するのは容易ではありません。ブラウザの標準挙動を網羅的に把握しなければならないからです。
実際には、既存の強力なエディタライブラリ(WYSIWYGエディタ)を利用することが多いです。たとえば次のようなものがあります。
これらのライブラリは、内部的には contenteditable
の仕組みやSelection APIなどを活用しつつ、Undo/Redo管理やプラグインシステム、コピーペーストの制御、複雑なスタイリングなどを整備しており、実装者の負担を大きく軽減してくれます。
複数の contenteditable
要素がある場合、どこにフォーカスがあるのかを管理することが重要です。フォーカスのある要素と選択範囲を適切に把握し、スタイリングやボタンのアクティブ状態を更新してユーザーにわかりやすくフィードバックを返す必要があります。
これはエディタライブラリにおいても大きなポイントであり、単純に contenteditable="true"
の要素を増やすだけでは管理が複雑になるケースがあります。
HTMLのままテキストを扱うのではなく、マークダウンや独自の文章構造で管理したい場合があります。その場合は、編集が終わったタイミングでHTMLをパースして、所望のテキスト形式に変換する必要があります。
逆に、マークダウンをHTMLに変換して contenteditable
上で表示・編集し、保存時に再度マークダウン形式に変換するといったフローも考えられます。いずれにしても、変換ロジックを正しく実装することが求められます。
contenteditable
は、ユーザーが自由にHTMLを入力できる仕組みのため、セキュリティリスクに注意しなければなりません。主に以下の点を考慮しましょう。
contenteditable
はHTML要素を簡易的なテキストエディタにできる便利な属性ですが、ブラウザ間での挙動の違いやリッチテキスト編集ゆえの複雑さが存在します。<div contenteditable="true">
のように指定し、その要素の innerHTML
や textContent
を取得して処理します。