Plusieurs éditeurs de texte enrichi sur une même page

RichTextEditor

Créez un formulaire à deux éditeurs avec identifiants uniques, valeurs HTML séparées et réinitialisation confirmée.

Conserver une instance par champ

Un formulaire produit peut avoir un résumé et une description. Créez un éditeur par conteneur et gardez les références dans une table. Chargez CSS, cœur et extensions une seule fois. Ne réutilisez ni identifiant ni variable unique pour tous les éditeurs. Chaque instance est un document indépendant, pas une racine du même document partagé.

Exécuter l’exemple complet

Servez les fichiers depuis votre application. L’exemple affiche le HTML actuel en JSON avec textContent, sans innerHTML. Il ne sauvegarde rien et ne fait aucune requête. Restaurer le résumé ne modifie pas les détails ; restaurer les deux exige confirmation. Toute modification efface la prévisualisation périmée.

<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>

Enregistrer et valider chaque champ

Utilisez des clés stables comme summary et details. Lisez les deux instances à l’envoi, quel que soit le dernier champ actif. Le serveur doit autoriser le dossier, valider et assainir chaque HTML. En cas d’erreur, conservez les deux brouillons et signalez le champ concerné. Décidez si l’enregistrement doit être atomique.

Coordonner sauvegarde automatique et réinitialisation

Utilisez une clé par document ou un enregistrement versionné pour les deux. Partager une clé locale peut écraser un brouillon. Une réinitialisation du navigateur n’annule pas une écriture serveur, ne supprime pas les fichiers et ne crée pas de sauvegarde de récupération. Définissez d’abord la file d’attente et les versions.

Liste de vérification

Modifiez les deux champs et changez le focus avant l’inspection. Restaurez un champ et vérifiez que l’autre HTML reste identique. Annulez la réinitialisation globale. Testez JSON long, emoji, clavier et écrans de 320px. Plusieurs barres complètes consomment mémoire et espace : mesurez votre formulaire avant de multiplier les instances.

À lire ensuite