Varios editores de texto enriquecido en una misma página

RichTextEditor

Crea un formulario con dos editores, ID únicos, HTML independiente y restablecimiento con confirmación.

Conserva una instancia por campo

Un formulario de producto puede tener resumen y descripción. Crea un editor por contenedor y guarda las referencias en un mapa. Carga CSS, núcleo y complementos una sola vez. No reutilices ID ni sobrescribas todas las instancias con una variable. Son documentos independientes, no raíces de un documento compartido.

Ejecuta el ejemplo completo

Sirve los archivos desde tu aplicación. El ejemplo muestra el HTML actual como JSON con textContent, sin innerHTML. No guarda ni envía peticiones. Restaurar el resumen deja intactos los detalles; restaurar ambos requiere confirmación. Las ediciones borran la vista previa antigua.

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

Guarda y valida cada campo

Usa claves estables como summary y details. Lee ambas instancias al enviar, sin depender del último foco. El servidor debe autorizar el registro, validar y sanear cada HTML. Ante errores conserva ambos borradores e indica el campo afectado. Decide si los dos campos deben guardarse atómicamente.

Coordina guardado automático y reinicio

Usa una clave por documento o un registro con versión para ambos. Compartir una clave local puede sobrescribir el otro borrador. Un reinicio del navegador no cancela escrituras del servidor, elimina archivos ni crea una copia de recuperación. Define primero la cola y la política de versiones.

Lista de aceptación

Edita ambos campos y cambia el foco antes de inspeccionar. Restaura uno y comprueba que el HTML del otro sea idéntico. Cancela el reinicio completo y conserva los borradores. Prueba JSON largo, emoji, teclado y pantallas de 320px. Muchas barras completas consumen memoria y espacio: mide el formulario real antes de crear decenas de instancias.

Para seguir leyendo