同じページで複数のリッチテキストエディターを使う

一意の ID、独立した HTML 値、安全なプレビュー、確認付きリセットを備えたフォームを作ります。
フィールドごとにインスタンスを保持する
商品フォームでは概要と詳細が必要になることがあります。各ホストにエディターを作り、参照をマップに保持します。CSS、コア、プラグインは一度だけ読み込みます。ID や変数を使い回さないでください。独立したインスタンスは別の文書であり、共有文書やマルチルートとは異なります。
完全なサンプルを実行する
アプリからファイルを配信します。最新の HTML を textContent で JSON として表示し、innerHTML は使いません。保存も通信も行いません。概要だけの復元は詳細に影響せず、両方の復元には確認が必要です。編集後は古いプレビューを消去します。
<link rel="stylesheet" href="/richtexteditor/rte_theme_default.css">
<script src="/richtexteditor/rte.js"></script>
<script src="/richtexteditor/plugins/all_plugins.js"></script>
<style>body{font:16px/1.6 system-ui;max-width:960px;margin:24px auto;padding:0 16px}section{min-width:0;margin-block:20px}.editor-scroll{max-width:100%;overflow-x:auto}button{min-height:44px;padding:8px 16px;margin:8px 8px 0 0}pre{white-space:pre-wrap;overflow-wrap:anywhere}</style>
<h1>Two editors, two independent fields</h1>
<form id="product-form">
<section aria-labelledby="summary-heading">
<h2 id="summary-heading">Summary</h2>
<div class="editor-scroll"><div id="summary-editor"></div></div>
<button type="button" id="restore-summary">Restore summary</button>
</section>
<section aria-labelledby="details-heading">
<h2 id="details-heading">Details</h2>
<div class="editor-scroll"><div id="details-editor"></div></div>
</section>
<button type="submit">Inspect both fields</button>
<button type="reset">Restore both fields</button>
<p id="status" role="status" aria-live="polite"></p>
<pre id="payload"></pre>
</form>
<script>
// Load the assets once. Give every host its own unique ID and instance.
const initial = {
summary: "<p>A short <strong>product summary</strong>.</p>",
details: "<h2>Details</h2><p>A separate document with its own formatting.</p>"
};
const editors = {
summary: new RichTextEditor("#summary-editor", { toolbar: "full", height: "240px" }),
details: new RichTextEditor("#details-editor", { toolbar: "full", height: "320px" })
};
const form = document.getElementById("product-form");
const status = document.getElementById("status");
const output = document.getElementById("payload");
function resetFields() {
for (const key of Object.keys(editors)) editors[key].setHTMLCode(initial[key]);
}
resetFields();
function invalidatePreview() {
output.textContent = "";
status.textContent = "Draft changed. Inspect again to see current values.";
}
for (const instance of Object.values(editors)) instance.attachEvent("change", invalidatePreview);
document.getElementById("restore-summary").addEventListener("click", () => {
if (!confirm("Restore only the summary?")) return;
editors.summary.setHTMLCode(initial.summary);
invalidatePreview();
});
form.addEventListener("reset", event => {
if (!confirm("Restore both documents?")) { event.preventDefault(); return; }
resetFields();
output.textContent = "";
status.textContent = "Both initial documents restored. Nothing saved.";
});
function collect() {
return Object.fromEntries(Object.entries(editors).map(([key, instance]) => [key, instance.getHTMLCode()]));
}
form.addEventListener("submit", event => {
event.preventDefault();
// Read both live instances now; never use whichever editor last had focus.
output.textContent = JSON.stringify(collect(), null, 2);
status.textContent = "Current fields shown below. No request sent.";
});
window.multipleDemo = { editors, initial, collect };
</script>
フィールドを明示して保存・検証する
サーバーとの契約には summary と details などの固定キーを使います。最後のフォーカスに頼らず、送信時に両方を読みます。実際の API はレコードの権限を確認し、各 HTML を検証・サニタイズします。エラー時は両方の下書きを保持し、問題のフィールドを示します。一括保存の必要性も決めてください。
自動保存とリセットを調整する
文書ごとに別の保存キーを使うか、全体をバージョン付きレコードにします。同じローカル保存キーは下書きを上書きします。ブラウザーのリセットはサーバー書き込みを取り消さず、画像削除や復旧用バックアップにもなりません。自動保存の前にキューとバージョン方針を決めます。
確認項目
両方を編集し、フォーカスを移して値を確認します。一方を復元して他方の HTML が同一であることを確認します。全体の復元をキャンセルして下書きを保持します。長い JSON、絵文字、キーボード、320px 画面も試します。多数のツールバーはメモリと場所を使うため、大量に初期化する前に実際のフォームを計測してください。