Web ページ上の見出しや段落を、その場でクリックして直接書き換えられたら便利だと思ったことはないでしょうか。テキストエリアや入力欄を用意しなくても、任意の HTML 要素に contenteditable という属性を1つ付けるだけで、その要素を編集可能にできます。この記事では、contenteditable の基本的な使い方から、指定できる値の違い、JavaScript で編集内容を取り出す方法、そして実際に運用するうえで気をつけたい点までを、触って試せるデモを交えて解説します。
目次
contenteditable で要素を編集可能にする
contenteditable は、その要素の中身をユーザーが直接編集できるようにするグローバル属性です。<div> や <p>、<h1> など、ほとんどすべての要素に付けられます。値に true(または空文字)を指定すると、その要素をクリックしたときにカーソルが入り、文字の入力・削除ができるようになります。まずは最小の例を見てみましょう。
<div contenteditable="true">
この部分をクリックすると、その場で文字を編集できます。
</div>
これだけで、ブラウザ上ではこの <div> がひとつの入力欄のように振る舞います。属性名だけを書いた contenteditable は contenteditable="true" と同じ意味になります。ただし後述するように、値を省略せずに true と明記しておいたほうが意図が伝わりやすく、おすすめです。
指定できる値の違い
contenteditable には、編集の可否や編集の仕方を切り替えるいくつかの値があります。それぞれの意味を整理しておきましょう。
| 値 | 意味 |
|---|---|
true(または空文字) | 要素を編集可能にする。書式付きの入力もできる |
false | 編集を明示的に禁止する。編集可能な親の中で一部だけ固定したいときに使う |
plaintext-only | 編集は許可するが、太字などの書式は付かず、プレーンテキストとして扱う |
inherit | 親要素の編集可否を引き継ぐ(初期値に相当) |
とくに使い分けのポイントになるのが false と plaintext-only です。contenteditable="true" を付けた要素の内側に置いた要素は、通常はその要素も一緒に編集できてしまいます。編集させたくない部分には contenteditable="false" を付けて固定します。また、書式付きのリッチな編集が不要で、単純なテキストだけを受け取りたい場合は plaintext-only が便利です。こちらは true に比べると対応ブラウザがやや限られるため、利用前に対応状況を確認しておくと安心です。
編集内容を JavaScript で取り出す
編集できるようにしただけでは、入力された内容はどこにも保存されません。<textarea> と違ってフォーム送信時に自動で送られることもないため、内容の取得と保存は自分で行う必要があります。編集のたびに発火する input イベントを使えば、変更をリアルタイムに受け取れます。下のデモは、編集エリアの文字数を input イベントで数えて表示するものです。文章をクリックして書き換えると、下の数字が変化します。
<div class="editor" contenteditable="true">この文章をクリックして、自由に書き換えてみてください。</div>
<p class="output">編集した文字数:<span id="count">0</span></p>
body {
font-family: sans-serif;
padding: 20px;
}
.editor {
min-height: 60px;
padding: 12px 16px;
border: 1px solid #ccc;
border-radius: 6px;
line-height: 1.7;
}
/* フォーカス時に編集中だと分かる見た目にする */
.editor:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
}
.output {
margin-top: 12px;
color: #555;
font-size: 14px;
}
const editor = document.querySelector('.editor');
const count = document.getElementById('count');
// input イベントは編集のたびに発火する
editor.addEventListener('input', () => {
// textContent で書式を除いた文字だけを取り出す
const text = editor.textContent;
count.textContent = text.length;
});
内容を取り出すときは、目的に応じて2つのプロパティを使い分けます。書式を無視して文字だけがほしいなら textContent、太字や改行などの HTML 構造ごと保存したいなら innerHTML を読みます。たとえば入力された内容をそのまま保存したい場合は、次のように書きます。
const editor = document.querySelector('.editor');
editor.addEventListener('input', () => {
// 書式を除いたテキストだけがほしいとき
const text = editor.textContent;
// 太字や改行など HTML ごと保存したいとき
const html = editor.innerHTML;
// 例:入力のたびにブラウザに一時保存する
localStorage.setItem('draft', html);
});
ここではブラウザの localStorage に保存する例を示しましたが、実際のアプリケーションでは、この値をサーバーに送信して保存することになります。繰り返しになりますが、保存の仕組みは自前で用意する必要がある点が、通常のフォーム部品との大きな違いです。
編集中だと分かる見た目にする
多くのブラウザでは、contenteditable な要素をクリックしてフォーカスすると、周囲に薄い枠線(アウトライン)が表示されます。これは「いま編集できる状態です」というユーザーへの合図なので、基本的には残しておくのがおすすめです。ただし、デザインに合わせて色や太さを調整したいこともあるでしょう。その場合は :focus 擬似クラスで outline を上書きします。
.editor:focus {
/* 編集中だと分かるように枠線を目立たせる */
outline: 2px solid #007bff;
outline-offset: 2px;
}
outline: none でアウトラインを完全に消すこともできますが、その場合はどこが編集中なのかが分からなくなります。消すなら、代わりに背景色や枠線を変えるなど、フォーカスが目で分かる別の手がかりを必ず用意してください。これはキーボードだけで操作する人にとって特に重要です。
使うときに気をつけたいこと
入力した HTML をそのまま表示しない
contenteditable で受け取った内容を innerHTML のまま保存し、それを別のページでそのまま表示すると、クロスサイトスクリプティング(XSS)の危険があります。ユーザーが <script> タグや onerror などの属性を仕込んだ場合、それが他の閲覧者のブラウザで実行されてしまうためです。編集内容を HTML として保存・再表示する場合は、サーバー側やライブラリで危険なタグ・属性を取り除く「サニタイズ」を必ず行ってください。書式が不要なら、はじめから textContent だけを扱うか plaintext-only を使うほうが安全です。
改行が div や br に変換される
contenteditable な要素の中で改行(Enter)を押すと、ブラウザはその改行を <div> や <br> といった要素として挿入します。どちらが挿入されるかはブラウザによって異なり、そのため innerHTML で取り出した中身には、自分では書いた覚えのないタグが混ざって見えることがあります。文章としての改行だけを扱いたいのであれば、textContent を読めばこうしたタグは含まれず、純粋なテキストとして改行を受け取れます。マークアップの差を意識したくないときは、textContent を基本にすると扱いが楽になります。
フォームでは自動送信されない
contenteditable な要素は <input> や <textarea> とは違い、<form> の中に置いても送信対象になりません。フォームと一緒に値を送りたい場合は、編集内容を JavaScript で取り出し、<input type="hidden"> の value に代入してから送信するなど、橋渡しの処理を自分で書く必要があります。
まとめ
contenteditable="true" を付けるだけで、任意の要素をその場で編集できるようになります。値には true / false / plaintext-only / inherit があり、一部だけ編集を禁止したいときは false、書式なしのテキストだけ扱いたいときは plaintext-only が役立ちます。編集内容は input イベントで受け取り、書式が不要なら textContent、HTML ごと保存するなら innerHTML を読みます。ただし保存の仕組みは自前で用意する必要があり、innerHTML をそのまま再表示する場合は XSS 対策のサニタイズが欠かせません。改行が <div> や <br> になる挙動も踏まえたうえで、手軽なインライン編集の手段として活用してみてください。