【実務・中級編】contenteditable属性によるインライン編集 – HTML実践ガイド

インライン編集の現在地:`contenteditable`を「事故らず」使いこなすための勘所

フロントエンド開発の現場で、「ここを直接編集できるようにしてほしい」という要望が上がったとき、真っ先に頭をよぎるのが `contenteditable` 属性ですよね。

「`contenteditable=”true”` を付けるだけ? 簡単じゃないか」

そう思って安易に実装した結果、後から「バックスペースを押したらDOM構造が崩れた」「ペーストしたテキストに不要なスタイルが混入した」といった地獄のようなバグ修正に追われた経験はありませんか?

今回は、ただ動くだけのコードではなく、プロダクション環境でも胸を張って使えるレベルの「インライン編集実装の作法」について、少し深掘りして解説します。

—

ブラウザが裏側で行っている「ブラックボックス」を理解する

まず押さえておきたいのは、`contenteditable` を有効にすると、ブラウザが裏側で何をしているか、という点です。

ブラウザのレンダリングエンジン(BlinkやWebKitなど)は、ユーザーの入力に対してその都度、独自の「編集コマンド」をDOMに適用します。これらはブラウザの実装に依存しているため、ChromeとSafariで生成されるHTML構造が微妙に異なるケースが多々あります。これが、インライン編集の難易度を跳ね上げる最大の要因です。

特に中級者以上のエンジニアが意識すべきは、「DOMの整合性」と「ユーザー入力の制御」の分離です。全てをブラウザ任せにすると、意図しない `

` や `
` が生成され、サーバーサイドへ送るデータが汚染されます。

—

実践:堅牢なインライン編集コンポーネント

では、現場でそのまま使える「綺麗で安全な」実装パターンを見ていきましょう。今回は `span` 要素を対象に、編集開始から確定、そしてサニタイズまでを考慮したコードです。

/

  • インライン編集を制御するクラス(あるいは関数)の雛形

/
const setupEditable = (element) => {
// 1. 編集可能にする
element.setAttribute(‘contenteditable’, ‘true’);

// 2. 編集確定時のイベントハンドリング
element.addEventListener(‘blur’, (e) => {
const target = e.target;

// 不要なタグの混入を防ぐ(text/plain以外は削ぎ落とすのが定石)
const cleanText = target.innerText.trim();

if (cleanText === “”) {
// 空なら元の値に戻す等のバリデーションが必要
console.warn(“空の値は保存できません”);
return;
}

// ここでAPIリクエスト等の保存処理を走らせる
console.log(“保存する値:”, cleanText);
});

// 3. ペースト時の制御(ここが最大の地雷原!)
element.addEventListener(‘paste’, (e) => {
e.preventDefault(); // デフォルトのペースト挙動を無効化

// クリップボードからプレーンテキストのみを取得
const text = e.clipboardData.getData(‘text/plain’);

// 現在のカーソル位置にテキストを挿入する
document.execCommand(‘insertText’, false, text);
});
};

// 使用例
const editableSpan = document.querySelector(‘.js-editable’);
setupEditable(editableSpan);

—

なぜこのコードが良いのか? 3つのポイント

1. `paste` イベントのインターセプト:
ユーザーがWebサイトやWordからコピーした際、HTMLタグやインラインスタイルがそのまま混入することがあります。`e.preventDefault()` して `insertText` を強制することで、不要なマークアップの侵入を確実にブロックできます。
2. `blur` イベントでの確定処理:
フォーカスが外れたタイミングで保存処理をフックします。`change` イベントは `contenteditable` 要素では発火しないため、ここを間違えて時間を溶かすのは新人あるあるです。必ず `blur` か `input` を使いましょう。
3. `innerText` の採用:
`innerHTML` を取得すると、内部で生成されたゴミタグまで拾ってしまう可能性があります。純粋な編集テキストを扱うなら `innerText` や `textContent` を使うのが安全です。

—

シニアからのアドバイス:さらなる高みへ

この実装に慣れたら、次は以下のステップを検討してください。

  • 状態管理との同期: ReactやVueなどのフレームワークを使っている場合、`contenteditable` の値とStateをどう同期させるかが肝です。`onInput` でStateを更新しつつ、カーソル位置(Selection API)を維持する実装は難易度が高いですが、挑戦する価値があります。
  • キーボード操作の制御: 「Enterキーを押したら改行させずに確定させたい」といったUIの微調整は、`keydown` イベントで `event.key === ‘Enter’` を拾い、`e.preventDefault()` して `blur()` を呼ぶだけで実現できます。

`contenteditable` は強力な機能ですが、ブラウザの「お節介」な挙動との戦いでもあります。今回紹介したような最小限の制御を積み重ねることで、堅牢なUIを作り上げてください。

「仕組みを理解し、制御下に置く」。これこそが、中級者から一歩抜け出すためのフロントエンド・エンジニアの矜持です。現場での実装、応援していますよ!

コメント

タイトルとURLをコピーしました