Convertir texto enriquecido a texto plano y descargar un TXT con JavaScript

Exporta el texto actual en TXT UTF-8 con vista previa segura y retirada de archivos obsoletos.
Elige entre texto y HTML
getPlainText() devuelve texto extraído y getHTMLCode() devuelve marcado. Quitar etiquetas con expresiones regulares puede unir palabras o dejar entidades codificadas. El texto plano pierde formato. Sirve como copia de lectura, entrada de índice o registro de soporte, pero no como copia de seguridad del documento.
Ejecuta el ejemplo completo
Sirve los archivos desde tu aplicación. Preparar lee el documento actual, normaliza CRLF y CR a LF y crea un Blob text/plain UTF-8 sin añadir BOM. Otro enlace descarga document.txt. La vista previa usa textContent y no interpreta HTML. Editar invalida el enlace anterior. No se envían peticiones al servidor.
<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}button,a{box-sizing:border-box;display:inline-flex;align-items:center;min-height:44px;padding:8px 16px}a[hidden]{display:none}pre{white-space:pre-wrap;overflow-wrap:anywhere}.editor-scroll{max-width:100%;overflow-x:auto}</style>
<h1>Export rich text as a UTF-8 text file</h1>
<div class="editor-scroll"><div id="editor"></div></div>
<button type="button" id="prepare">Prepare text file</button>
<a id="download" download="document.txt" hidden>Download .txt</a>
<p id="status" role="status" aria-live="polite"></p>
<pre id="preview"></pre>
<script>
const editor = new RichTextEditor("#editor", { toolbar: "full", height: "320px" });
editor.setHTMLCode("<h2>Project notes</h2><p>Keep the words, including <strong>formatting</strong>, 中文 and 👋.</p>");
const link = document.getElementById("download");
const preview = document.getElementById("preview");
const status = document.getElementById("status");
let currentURL = null, preparedHTML = null;
function clearPreparedFile() {
if (currentURL) {
const obsoleteURL = currentURL;
// Do not revoke synchronously: a download may still be starting.
setTimeout(() => URL.revokeObjectURL(obsoleteURL), 30000);
currentURL = null;
}
preparedHTML = null;
link.removeAttribute("href");
link.hidden = true;
preview.textContent = "";
}
function prepare() {
clearPreparedFile();
// Export the API result, not the HTML source or a regex-stripped version.
const text = editor.getPlainText().replace(/\r\n?/g, "\n");
preparedHTML = editor.getHTMLCode();
const file = new Blob([text], { type: "text/plain;charset=utf-8" });
currentURL = URL.createObjectURL(file);
link.href = currentURL;
link.hidden = false;
preview.textContent = text;
status.textContent = "Text file ready: " + file.size + " UTF-8 bytes. Click Download .txt. Nothing sent to a server.";
}
document.getElementById("prepare").addEventListener("click", prepare);
editor.attachEvent("change", () => {
// Delayed notifications for the same saved HTML do not invalidate this snapshot.
if (preparedHTML !== null && editor.getHTMLCode() === preparedHTML) return;
clearPreparedFile();
status.textContent = "Content changed. Prepare a new file before downloading.";
});
window.textDemo = { editor, prepare };
</script>
Comprende qué se pierde
Párrafos, marcadores de lista, separadores de tabla, alternativas de imágenes y enlaces dependen de la extracción. Prueba documentos reales sin prometer un resultado idéntico a Markdown o Word. TXT no conserva imágenes, estilos, comentarios, historial de cambios ni HTML editable. Guarda el HTML original para recuperar borradores. Un editor vacío puede devolver saltos de línea. El ejemplo conserva los espacios extraídos sin recortarlos; cambiar el foco puede afectar las líneas finales.
Gestiona la vida del archivo
Una URL de objeto retiene el Blob en memoria. El ejemplo libera URL obsoletas tras 30 segundos, no justo al pulsar. El navegador libera las restantes al salir. El plazo no es una señal universal de descarga terminada. Prueba descargas repetidas, documentos grandes y dispositivos objetivo. download sugiere un nombre; el navegador decide cómo guardar. Las descargas repetidas pueden requerir permiso del navegador. No eludas sus políticas ni inicies lotes sin intención del usuario.
Lista de aceptación
Exporta emoji, acentos combinados, chino, japonés, párrafos y tablas. Compara los bytes con getPlainText() normalizado, no con HTML. Prueba documentos vacíos y texto parecido a etiquetas. Editar tras preparar debe ocultar el enlace antiguo. TXT no sustituye escape CSV, saneamiento HTML ni validación del servidor.