【実務・中級編】contenteditable属性とテキスト要素の編集 – HTML実践ガイド

`contenteditable`の闇を覗く:ブラウザごとの「改行」という名のカオスを制御する

フロントエンドの現場で、「リッチテキストエディタを作ってくれ」という要件ほど、シニアエンジニアを憂鬱にさせるものはありません。一見すると `contenteditable=”true”` を当てるだけの簡単な仕事に見えますよね?

しかし、現実は甘くない。特に `p` や `pre` 要素を直接編集可能にした瞬間、ブラウザという名の「気まぐれな職人」たちが、それぞれ独自の解釈でDOMを破壊し始めます。今回は、この泥沼を回避するための「ブラウザの裏側」と、現場で生き残るための実装テクニックを共有します。

—

なぜブラウザは「改行」を解釈できないのか

`contenteditable` を有効にした要素内で Enter キーを押したとき、ブラウザは何を生成するのか。これが最大の罠です。

実は、仕様上は「ブラウザにお任せ」という部分が大きく、以下のようなバラつきが生じます。

  • Chrome/Safari: 基本的に `
    ` で囲むか、`
    ` を挿入する。
  • Firefox: `br` を好む傾向があるが、状況によって `

    ` を生成することもある。

  • IE/Edge(旧): 過去には独自仕様の極みでしたが、今はChromiumベースになりだいぶマシになりました。

特に厄介なのが、「ユーザーが改行したときに、ブラウザが何を生成するか制御不能」という点です。ある人は `

` で囲んでほしいのに、あるブラウザは `
` を連打する。この不統一が、後続のデータ保存時やバリデーション処理で大爆発を起こします。

—

実践:`contenteditable` を制御する最適解

もし、テキストの構造(段落分けなど)を厳密に管理したいのであれば、ブラウザのデフォルト挙動をそのまま許容してはいけません。以下のコードは、改行時に発生する不要なタグ生成を最小限に抑え、クリーンな状態を保つためのプロトタイプです。

/

  • contenteditable要素の改行挙動を制御する
  • ブラウザのデフォルト挙動を上書きし、一貫性を保つためのアプローチ

/
const editor = document.querySelector(‘.editable-area’);

editor.addEventListener(‘keydown’, (e) => {
// Enterキーが押された時の挙動をハックする
if (e.key === ‘Enter’) {
// もしshiftKeyを押していなければ、独自の改行処理を差し込むことも可能
// ブラウザごとのバラつきを抑えるための第一歩
console.log(‘Enterが押されました。DOM構造を確認してください。’);
}
});

// 入力内容の変化を監視し、不正なタグをクリーニングする
editor.addEventListener(‘input’, () => {
// ここでDOMをサニタイズする処理を挟むのが定石
// 例:
の連続を排除したり、勝手に挿入されたdivをpに変換するなど
const content = editor.innerHTML;

// 開発中のデバッグ用ログ
// 実際には DOMPurify などのライブラリを通すことを強く推奨
console.debug(‘現在のDOM構造:’, content);
});

—

`pre` 要素を編集する際の「地雷」

`pre` 要素はホワイトスペースを維持してくれるため、コードエディタ風のUIを作る際に重宝しますが、`contenteditable` と組み合わせると悲劇が起きます。

`pre` の中で Enter を押すと、多くのブラウザが中に `
` を詰め込みます。`pre` の中にあるべきは本来「改行コード(`\n`)」ですが、DOMとしては「要素」として扱われてしまうのです。

現場での回避策:

1. `white-space: pre-wrap;` を使う: 普通の `div` や `p` にこのスタイルを当てて、擬似的に `pre` の挙動を再現します。DOM構造がフラットになり、ブラウザの挙動を制御しやすくなります。
2. イベントを完全に掌握する: `beforeinput` イベントを監視し、`inputType` が `insertLineBreak` の場合に `e.preventDefault()` して、自前で `document.createTextNode(‘\n’)` を挿入する。これを行うだけで、ブラウザが勝手に生成するDOMのノイズを完全に排除できます。

—

シニアからのアドバイス:車輪の再発明は避けよ

ここまで実装の話をしておいて逆説的ですが、もしビジネス要件として「本格的なリッチテキストエディタ」が必要なら、自前で `contenteditable` を制御するのは絶対に避けてください。

ブラウザ間の差異、IME変換時の挙動、コピペ時のHTML汚染……これらは個人のエンジニアが解決できるレベルの「バグ」ではありません。

  • [TipTap](https://tiptap.dev/): Prosemirrorをベースにした、ヘッドレスで拡張性の高い最強の選択肢。
  • [Quill](https://quilljs.com/): 歴史があり、ドキュメントが充実している。

これらのライブラリは、ブラウザが吐き出すカオスなDOMを抽象化し、一貫したJSON形式のドキュメントとして扱えるようにしてくれます。

まとめ

`contenteditable` は強力な機能ですが、その裏側にあるブラウザの実装差異を理解せずに使うと、必ず負債になります。「DOMをどう扱うか」という低レイヤーな意識を持ちつつも、実務ではいかに「ブラウザの気まぐれを許容しない仕組みを作るか」を優先してください。

もしあなたが今、`contenteditable` と格闘しているなら、まずは `input` イベントで生成された innerHTML をコンソールに吐き出し、ブラウザがどれだけ「余計なこと」をしているかを観察してみてください。そこからが、本当のフロントエンド・エンジニアリングの始まりです。

コメント

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