Créer un bouton de copie pour les blocs de code
Si vous avez déjà observé quelqu’un peiner à sélectionner manuellement et copier un extrait de code — ou si vous l’avez vécu vous-même — vous savez déjà pourquoi un bouton de copie pour les blocs de code est important. C’est un petit détail d’interface utilisateur qui fait une vraie différence, en particulier pour les utilisateurs qui naviguent au clavier, par commandes vocales ou sur écrans tactiles.
Cet article vous guide dans la création d’une interface de copie d’extrait de code propre et fiable, en utilisant l’API Clipboard moderne — sans aucune bibliothèque requise.
Points clés à retenir
- Utilisez
navigator.clipboard.writeText()pour une implémentation moderne basée sur les promesses, qui nécessite un contexte sécurisé (HTTPS ou localhost) et un événement déclenché par l’utilisateur. - Extrayez le code avec
textContent, et noninnerHTML, afin d’éviter de copier les balises<span>ajoutées par les coloriseurs syntaxiques avec le code lui-même. - Encapsulez l’appel dans un bloc
try/catchpour gérer les erreurs de permission et fournir un retour visuel clair aux utilisateurs. - Améliorez l’accessibilité en ajoutant un
aria-labelau bouton, particulièrement lorsque des icônes remplacent les libellés textuels. - La méthode obsolète
document.execCommand('copy')ne devrait être utilisée qu’en dernier recours, comme solution de repli pour les environnements anciens.
Fonctionnement de la méthode writeText de l’API Clipboard
L’approche moderne pour copier dans le presse-papiers en JavaScript est navigator.clipboard.writeText(). Elle est basée sur les promesses, asynchrone et prise en charge par tous les navigateurs actuels.
Deux points à connaître avant de l’utiliser :
- Elle requiert un contexte sécurisé. L’API Clipboard ne fonctionne qu’en HTTPS. Sur
localhost, le HTTP simple est également considéré comme sécurisé, ce qui convient parfaitement pour le développement. - Elle doit être déclenchée par une action utilisateur. L’appeler à l’intérieur d’un gestionnaire d’événement
clicksatisfait automatiquement cette exigence.
await navigator.clipboard.writeText("your text here");
C’est l’essentiel de la fonctionnalité.
Extraire le texte d’un bloc de code
Votre HTML ressemble probablement à ceci :
<pre><code>const greeting = "hello";</code></pre>
Pour récupérer le texte brut, utilisez textContent — et non innerHTML. Utiliser innerHTML copierait les balises HTML en même temps que le code, ce qui n’est jamais souhaité.
const code = document.querySelector("pre code").textContent;
Si vos blocs de code utilisent un coloriseur syntaxique comme Prism.js ou Highlight.js, celui-ci encapsule les jetons dans des éléments <span>. textContent élimine tout cela et ne renvoie que le texte brut, ce qui est exactement ce qui doit aboutir dans le presse-papiers de l’utilisateur.
Discover how at OpenReplay.com.
Construire le bouton de copie pour les blocs de code
Voici une implémentation complète et fonctionnelle :
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);
});
Quelques points à noter ici :
aria-labeldonne au bouton un nom accessible pour les lecteurs d’écran, ce qui est particulièrement important si vous remplacez ultérieurement le texte par une icône.e.currentTargetest plus sûr quee.targetlorsque votre bouton contient des éléments enfants comme une icône SVG —e.targetpeut pointer vers l’icône elle-même plutôt que vers le bouton. (Vous pouvez y accéder en ajoutant le paramètreeventau gestionnaire de clic si nécessaire.)- Le bloc
try/catchgère élégamment les refus de permission ou les défaillances inattendues, en offrant aux utilisateurs un retour visible plutôt qu’une erreur silencieuse.
Qu’en est-il des navigateurs plus anciens ?
navigator.clipboard bénéficie d’une excellente prise en charge sur tous les navigateurs modernes. Si vous devez prendre en charge des environnements plus anciens, document.execCommand('copy') existe comme solution de repli — mais elle est obsolète et peu fiable. Ne l’utilisez qu’en dernier recours, derrière une vérification de fonctionnalité :
if (!navigator.clipboard) {
// legacy fallback using document.execCommand('copy')
}
Pour la plupart des sites de documentation et des outils de développement créés aujourd’hui, vous pouvez vous appuyer en toute confiance sur l’API Clipboard seule.
Conclusion
Un bouton de copie fonctionnel pour les blocs de code se résume à trois choses : récupérer le texte avec textContent, l’écrire avec navigator.clipboard.writeText(), et fournir à l’utilisateur un retour clair en cas de succès ou d’échec. Rendez le bouton accessible grâce à un aria-label, gérez les erreurs explicitement, et vous obtiendrez une implémentation prête pour la production en moins de 30 lignes de JavaScript pur.
FAQ
L'API Clipboard exige un contexte sécurisé, ce qui signifie HTTPS en production. Cependant, les navigateurs considèrent par défaut localhost comme sécurisé, donc le HTTP simple fonctionne sans problème pendant le développement. Si vous testez sur une adresse IP de réseau local comme 192.168.x.x, l'API échouera car elle n'est pas considérée comme sécurisée. Utilisez plutôt localhost ou configurez un serveur de développement HTTPS.
Non. L'API Clipboard exige une activation par l'utilisateur, ce qui signifie que l'appel doit avoir lieu à l'intérieur d'un gestionnaire d'événement déclenché par l'utilisateur, comme click, keydown ou pointerup. Appeler writeText au chargement de la page ou dans un setTimeout échouera silencieusement ou lèvera une erreur de permission. Cette restriction empêche les sites malveillants de détourner le presse-papiers sans consentement.
La propriété textContent préserve déjà les espaces blancs, y compris les sauts de ligne et l'indentation, exactement tels qu'ils sont écrits dans votre HTML. Tant que vos éléments pre et code contiennent du code source correctement formaté, le texte copié correspondra. Évitez d'utiliser innerText, qui peut normaliser les espaces blancs en fonction du rendu CSS et produire des résultats incohérents entre les navigateurs.
e.target fait référence à l'élément qui a effectivement reçu le clic, qui peut être un élément enfant comme une icône SVG à l'intérieur de votre bouton. e.currentTarget fait toujours référence à l'élément auquel l'écouteur d'événement a été attaché, c'est-à-dire le bouton lui-même dans ce cas. Utiliser currentTarget évite des bugs lorsque les utilisateurs cliquent sur des icônes ou des spans imbriqués dans le bouton.
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.