Criando um Botão de Copiar para Blocos de Código
Se você já viu alguém ter dificuldade em destacar e copiar manualmente um trecho de código — ou já passou por isso você mesmo — já entende por que um botão de copiar para blocos de código importa. É um pequeno detalhe de UI que faz uma diferença real, especialmente para usuários que navegam por teclado, comandos de voz ou telas sensíveis ao toque.
Este artigo guia você na construção de uma UI limpa e confiável para copiar trechos de código usando a moderna Clipboard API — sem necessidade de bibliotecas.
Principais Pontos
- Use
navigator.clipboard.writeText()para uma implementação moderna e baseada em promises, que requer um contexto seguro (HTTPS ou localhost) e um evento iniciado pelo usuário. - Extraia o código com
textContent, nãoinnerHTML, para evitar copiar wrappers<span>do realce de sintaxe junto com o código real. - Envolva a chamada em um bloco
try/catchpara lidar com erros de permissão e fornecer feedback visual claro aos usuários. - Melhore a acessibilidade adicionando um
aria-labelao botão, especialmente quando ícones substituem rótulos de texto. - O obsoleto
document.execCommand('copy')deve ser usado apenas como fallback de último recurso para ambientes legados.
Como Funciona o Método writeText da Clipboard API
A abordagem moderna para copiar para a área de transferência em JavaScript é navigator.clipboard.writeText(). É baseada em promises, assíncrona e suportada em todos os navegadores atuais.
Duas coisas que você deve saber antes de usá-la:
- Ela requer um contexto seguro. A Clipboard API só funciona sobre HTTPS. No
localhost, HTTP simples também é tratado como seguro, então funciona bem para desenvolvimento. - Deve ser acionada por uma ação do usuário. Chamá-la dentro de um manipulador de evento
clicksatisfaz esse requisito automaticamente.
await navigator.clipboard.writeText("your text here");
Esse é o núcleo de toda a funcionalidade.
Extraindo Texto de um Bloco de Código
Seu HTML provavelmente é parecido com isto:
<pre><code>const greeting = "hello";</code></pre>
Para obter o texto bruto, use textContent — e não innerHTML. Usar innerHTML copiaria as tags HTML junto com o código, o que nunca é o desejado.
const code = document.querySelector("pre code").textContent;
Se seus blocos de código usam um realçador de sintaxe como Prism.js ou Highlight.js, o realçador envolve os tokens em elementos <span>. O textContent remove tudo isso e retorna apenas o texto puro, que é exatamente o que deve ir para a área de transferência do usuário.
Discover how at OpenReplay.com.
Construindo o Botão de Copiar para Blocos de Código
Aqui está uma implementação completa e funcional:
document.querySelectorAll("pre").forEach((block) => {
const button = document.createElement("button");
button.type = "button";
button.textContent = "Copy";
button.setAttribute("aria-label", "Copy code to clipboard");
button.addEventListener("click", async () => {
const code = block.querySelector("code")?.textContent ?? "";
try {
await navigator.clipboard.writeText(code);
button.textContent = "Copied!";
setTimeout(() => (button.textContent = "Copy"), 2000);
} catch (err) {
console.error("Copy failed:", err);
button.textContent = "Failed";
setTimeout(() => (button.textContent = "Copy"), 2000);
}
});
block.style.position = "relative";
block.appendChild(button);
});
Alguns pontos que vale a pena destacar:
aria-labeldá ao botão um nome acessível para leitores de tela, especialmente importante caso você posteriormente substitua o texto por um ícone.e.currentTargeté mais seguro quee.targetquando seu botão contém elementos filhos, como um ícone SVG —e.targetpode apontar para o próprio ícone em vez do botão. (Você pode acessá-lo adicionando o parâmetroeventao manipulador de clique, se necessário.)- O bloco
try/catchlida com negações de permissão ou falhas inesperadas de forma elegante, oferecendo feedback visível ao usuário em vez de um erro silencioso.
E os Navegadores Antigos?
navigator.clipboard tem excelente suporte nos navegadores modernos. Se você precisa dar suporte a ambientes mais antigos, document.execCommand('copy') existe como fallback — mas está obsoleto e é pouco confiável. Use-o apenas como último recurso, atrás de uma verificação de funcionalidade:
if (!navigator.clipboard) {
// legacy fallback using document.execCommand('copy')
}
Para a maioria dos sites de documentação e ferramentas de desenvolvedor construídos hoje, você pode confiar com segurança apenas na Clipboard API.
Conclusão
Um botão funcional de copiar para blocos de código se resume a três coisas: pegar o texto com textContent, gravá-lo com navigator.clipboard.writeText() e dar ao usuário um feedback claro sobre o sucesso ou falha. Mantenha o botão acessível com um aria-label, trate os erros explicitamente, e você terá uma implementação pronta para produção em menos de 30 linhas de JavaScript puro.
Perguntas Frequentes
A Clipboard API requer um contexto seguro, o que significa HTTPS em produção. No entanto, os navegadores tratam localhost como seguro por padrão, então HTTP simples funciona bem durante o desenvolvimento. Se você estiver testando em um IP de rede local como 192.168.x.x, a API falhará porque isso não é considerado seguro. Use localhost ou configure um servidor de desenvolvimento HTTPS.
Não. A Clipboard API requer ativação pelo usuário, ou seja, a chamada deve ocorrer dentro de um manipulador de evento acionado pelo usuário, como click, keydown ou pointerup. Chamar writeText no carregamento da página ou dentro de um setTimeout falhará silenciosamente ou lançará um erro de permissão. Essa restrição impede que sites maliciosos sequestrem a área de transferência sem consentimento.
A propriedade textContent já preserva os espaços em branco, incluindo quebras de linha e indentação, exatamente como escritos no seu HTML. Desde que seus elementos pre e code contenham código-fonte formatado corretamente, o texto copiado será idêntico. Evite usar innerText, que pode normalizar espaços em branco com base na renderização CSS e produzir resultados inconsistentes entre navegadores.
e.target refere-se ao elemento que efetivamente recebeu o clique, que pode ser um elemento filho como um ícone SVG dentro do seu botão. e.currentTarget sempre se refere ao elemento ao qual o ouvinte de evento foi anexado, neste caso, o próprio botão. Usar currentTarget evita bugs quando os usuários clicam em ícones ou spans aninhados dentro do botão.
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.