Editor rico que não carregou: a textarea que garante que a pessoa ainda escreve
Comece o formulário com uma textarea funcional, deixe o editor rico assumir só se carregar e faça o servidor aceitar texto puro ou HTML, para um CDN fora do ar não impedir ninguém de salvar.
O CDN do editor rico ficou fora do ar por quarenta minutos numa quarta à tarde. Nesse intervalo, a área de escrever post do blog da empresa apareceu como um retângulo morto: não dava para digitar, e quem tinha começado antes clicou em Salvar e gravou vazio. A resposta curta: o HTML da página já traz uma textarea funcional com o conteúdo. O script do editor, se carregar, esconde a textarea e assume. Se não carregar, a pessoa escreve texto puro e envia normalmente, e o servidor aceita os dois formatos, limpando o HTML quando houver. A barra de ferramentas é melhoria; escrever e salvar é a função. Se o seu problema é o editor que salva vazio, veja também como sincronizar e sanitizar o HTML do editor rico.
Esse padrão tem nome antigo, melhoria progressiva, e continua sendo o jeito mais barato de não depender de um arquivo de terceiros para a tarefa principal da tela.
Por que noscript não resolve
A primeira ideia costuma ser um bloco noscript com a textarea. Ele só aparece quando o JavaScript está desligado no navegador, caso raro. Os casos reais são outros: o script está ligado, mas o arquivo não chegou. O CDN caiu, o firewall da empresa bloqueia o domínio, uma extensão de bloqueio barrou o arquivo, a conexão do celular cortou no meio, ou um erro em outro script da página interrompeu a execução antes do editor montar. Em todos esses casos, noscript fica invisível e a área de texto continua morta.
O problema de fundo é a ordem. Se a página nasce com uma div vazia esperando o editor, ela depende do editor para existir. Se nasce com uma textarea, o editor é um passo opcional por cima de algo que já funciona.
Como montar o formulário que funciona sem o editor
O exemplo continua o do post sobre editor rico que salva vazio, que mostra como sincronizar a div editável com o formulário. Aqui, o foco é o que acontece quando o editor nem chega a montar.
O HTML de partida
<form method="post" action="/posts">
<label for="corpo">Texto do post</label>
<textarea id="corpo" name="corpo" rows="14" required></textarea>
<input type="hidden" id="formato" name="formato" value="texto">
<button type="submit">Salvar</button>
</form>
<script type="module" src="/js/ativar-editor.js"></script>
Sem nenhum script, esse formulário já cumpre a tarefa: a pessoa escreve, clica e o servidor recebe corpo e formato=texto.
O script que assume só se puder
O módulo /js/editor-rico.js representa o seu editor; ele exporta criarEditor, que monta a área editável a partir da textarea e devolve um objeto com o método html().
// /js/ativar-editor.js
const area = document.getElementById('corpo');
const formato = document.getElementById('formato');
try {
const { criarEditor } = await import('/js/editor-rico.js');
const editor = criarEditor(area);
area.required = false;
area.hidden = true;
formato.value = 'html';
area.form.addEventListener('submit', () => {
area.value = editor.html();
});
} catch (erro) {
console.warn('Editor rico indisponível, mantendo a textarea.', erro);
}
Dois detalhes evitam bugs difíceis. A textarea escondida com hidden continua sendo enviada no formulário, por isso ela segue como o campo que viaja. E uma textarea escondida que ainda tem required bloqueia o envio: o navegador tenta mostrar o erro num campo que não pode receber foco, e o botão Salvar parece não fazer nada.
Se o editor vem de CDN, servir o arquivo do seu próprio domínio reduz bastante o risco. Não elimina: um erro de JavaScript ou uma atualização quebrada ainda caem no catch, e é para isso que ele existe.
O servidor que aceita os dois formatos
const sanitizeHtml = require('sanitize-html');
const regras = {
allowedTags: ['p', 'br', 'strong', 'em', 'ul', 'ol', 'li', 'a', 'h2', 'h3', 'blockquote'],
allowedAttributes: { a: ['href'] },
allowedSchemes: ['http', 'https', 'mailto']
};
function escaparHtml(texto) {
return texto
.replaceAll('&', '&')
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll('"', '"');
}
function textoParaHtml(texto) {
return texto
.split(/\r?\n\s*\r?\n/)
.map((bloco) => bloco.trim())
.filter((bloco) => bloco !== '')
.map((bloco) => '<p>' + escaparHtml(bloco).split(/\r?\n/).join('<br>') + '</p>')
.join('');
}
function corpoParaGravar(corpo, formato) {
const entrada = typeof corpo === 'string' ? corpo : '';
return formato === 'html' ? sanitizeHtml(entrada, regras) : textoParaHtml(entrada);
}
Linha em branco vira parágrafo e quebra simples vira br. Quem escreveu na textarea não precisa saber HTML, e o texto aparece no site com a mesma estrutura do que foi digitado. O campo formato é só uma dica: mesmo que alguém o altere à mão, os dois caminhos produzem HTML seguro.
Editando um post que já tem formatação
Na criação, a textarea começa vazia e não há dilema. Na edição de um post com negrito e links, o servidor preenche a textarea com o HTML guardado e o campo formato com html. Se o editor carregar, ele assume e ninguém vê as marcações. Se não carregar, a pessoa vê as tags, o que é feio, mas preserva toda a formatação e ainda permite corrigir uma palavra com urgência. Mostre uma linha de ajuda explicando isso só no modo de reserva.
Como testar a degradação
No Chrome ou no Edge, o DevTools tem um painel de bloqueio de requisições. Bloqueie o endereço do editor, recarregue a página, escreva dois parágrafos e salve. Depois confira no banco que o texto chegou como dois parágrafos. Repita com a rede em modo lento, para ver se a pessoa consegue começar a digitar antes de o editor montar. Para envios sem recarregar a página, o post enviar formulário sem recarregar mostra como não perder o erro no caminho.
Erros comuns
Checklist
Bloqueie hoje o domínio do editor no seu navegador e tente publicar um rascunho. Ainda dá para escrever e salvar? Se não dá, a tela não tem degradação, tem dependência.
Referência: http://stackoverflow.com/questions/16941360/how-to-create-a-placeholder-for-div-that-act-like-textfield