Converter texto rico em texto simples e baixar TXT com JavaScript

Exporte o texto atual em TXT UTF-8 com prévia segura e invalidação de arquivos antigos.
Escolha entre texto e HTML
getPlainText() retorna texto extraído; getHTMLCode() retorna marcação. Remover tags com expressões regulares pode juntar palavras ou deixar entidades codificadas. Texto simples perde formatação. Serve como cópia de leitura, entrada de índice ou registro de suporte, mas não como backup do documento rico.
Execute o exemplo completo
Sirva os arquivos pela aplicação. Preparar lê o documento atual, normaliza CRLF e CR para LF e cria um Blob text/plain UTF-8 sem adicionar BOM. Um link baixa document.txt. A prévia usa textContent e não interpreta HTML. Editar invalida o link antigo. Nenhuma requisição é enviada ao 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>
Entenda o que se perde
Parágrafos, marcadores de lista, separadores de tabela, alternativas de imagens e links dependem da extração. Teste documentos reais sem prometer igualdade com Markdown ou Word. TXT não mantém imagens, estilos, comentários, histórico de alterações nem HTML editável. Preserve o HTML original para recuperar rascunhos. Um editor vazio pode retornar quebras de linha. O exemplo preserva os espaços extraídos sem aparar; mudanças de foco podem afetar linhas finais.
Gerencie a vida do arquivo
Uma URL de objeto mantém o Blob na memória. O exemplo libera URLs antigas depois de 30 segundos, não no instante do clique. O navegador libera as restantes ao sair da página. O prazo não é um sinal universal de download concluído. Teste downloads repetidos, documentos grandes e dispositivos-alvo. download sugere um nome; o navegador decide como salvar. Downloads repetidos podem exigir permissão do navegador. Não contorne essa política nem inicie lotes sem a intenção do usuário.
Lista de aceitação
Exporte emoji, acentos combinados, chinês, japonês, parágrafos e tabelas. Compare os bytes com getPlainText() normalizado, não com HTML. Teste documento vazio e texto parecido com tags. Editar após preparar deve ocultar o link antigo. TXT não substitui escape CSV, sanitização HTML nem validação do servidor.