Accès plus sûr et débloqué au presse-papiers pour le texte et les images
Le moyen traditionnel d'accéder au presse-papiers
du système consistait à utiliser
document.execCommand()
pour les interactions avec le presse-papiers. Bien qu’elle soit largement acceptée, cette méthode de découpe et
le collage était payant: l'accès au presse-papiers était synchrone et ne pouvait que lire
et écrire dans le DOM.
Cela convient pour de petits morceaux de texte, mais dans de nombreux cas, le blocage
pour transférer le presse-papiers est une mauvaise expérience. Désinfection ou nettoyage
un décodage des images peut être nécessaire
avant de pouvoir coller le contenu en toute sécurité. Le navigateur
peut avoir besoin de charger ou d'intégrer des ressources liées à partir d'un document collé. Ce serait
bloquer la page en attendant
le disque ou le réseau. Imaginez ajouter des autorisations
au sein de la combinaison, ce qui oblige le navigateur à bloquer la page tout en demandant
l'accès au presse-papiers. Dans le même temps, les autorisations mises en place
Les document.execCommand()
pour l'interaction avec le presse-papiers sont définis de manière vague et varient
entre les navigateurs.
La API Async Clipboard permet de résoudre ces problèmes en fournissant un modèle d'autorisations bloquer la page. L'API Async Clipboard est limitée à la gestion du texte et des images. sur la plupart des navigateurs, mais la prise en charge varie. Étudiez attentivement le navigateur de la compatibilité pour chacune des sections suivantes.
Copier: écriture des données dans le presse-papiers
writeText()
Pour copier du texte dans le presse-papiers, appelez writeText()
. Puisque cette API est
asynchrone, la fonction writeText()
renvoie une promesse qui résout ou
rejette selon que le texte transmis a bien été copié ou non:
async function copyPageUrl() {
try {
await navigator.clipboard.writeText(location.href);
console.log('Page URL copied to clipboard');
} catch (err) {
console.error('Failed to copy: ', err);
}
}
write()
En fait, writeText()
n'est qu'une méthode pratique pour la write()
générique
, qui vous permet également de copier des images dans le presse-papiers. Comme writeText()
, il
est asynchrone et renvoie une promesse.
Pour écrire une image dans le presse-papiers, vous avez besoin de l'image en tant que
blob
Une façon de faire
consiste à demander l'image à un serveur à l'aide de fetch()
, puis à appeler
blob()
sur le
de réponse.
Il n'est pas toujours souhaitable ou possible de demander une image au serveur
pour diverses raisons. Heureusement, vous pouvez également
dessiner l'image sur un canevas et
appeler le canevas
toBlob()
.
Transmettez ensuite un tableau d'objets ClipboardItem
en tant que paramètre à write()
.
. Actuellement, vous ne pouvez transmettre qu'une image à la fois, mais nous espérons ajouter
la prise en charge de plusieurs images à l'avenir. ClipboardItem
utilise un objet avec
le type MIME de l'image en tant que clé et le blob en tant que valeur. Pour blob
objets obtenus à partir de fetch()
ou canvas.toBlob()
, la propriété blob.type
contient automatiquement le type MIME approprié pour une image.
try {
const imgURL = '/images/generic/file.png';
const data = await fetch(imgURL);
const blob = await data.blob();
await navigator.clipboard.write([
new ClipboardItem({
// The key is determined dynamically based on the blob's type.
[blob.type]: blob
})
]);
console.log('Image copied.');
} catch (err) {
console.error(err.name, err.message);
}
Vous pouvez également écrire une promesse dans l'objet ClipboardItem
.
Pour ce format, vous devez connaître au préalable le type MIME des données.
try {
const imgURL = '/images/generic/file.png';
await navigator.clipboard.write([
new ClipboardItem({
// Set the key beforehand and write a promise as the value.
'image/png': fetch(imgURL).then(response => response.blob()),
})
]);
console.log('Image copied.');
} catch (err) {
console.error(err.name, err.message);
}
Copier l'événement
Lorsqu'un utilisateur lance une copie du presse-papiers
et n'appelle pas preventDefault()
, la
copy
événement
inclut une propriété clipboardData
dont les éléments sont déjà au bon format ;
Si vous souhaitez implémenter votre propre logique, vous devez appeler preventDefault()
pour
empêcher le comportement par défaut
au profit de votre propre implémentation.
Dans ce cas, clipboardData
sera vide.
Prenons l’exemple d’une page avec du texte et une image, et lorsque l’utilisateur sélectionne tout et
lance une copie du presse-papiers, votre solution personnalisée doit supprimer le texte et ne
copier l'image. Pour ce faire, reportez-vous à l'exemple de code ci-dessous.
Cet exemple ne montre pas
comment revenir à une version antérieure
lorsque l'API Clipboard n'est pas compatible.
<!-- The image we want on the clipboard. -->
<img src="kitten.webp" alt="Cute kitten.">
<!-- Some text we're not interested in. -->
<p>Lorem ipsum</p>
document.addEventListener("copy", async (e) => {
// Prevent the default behavior.
e.preventDefault();
try {
// Prepare an array for the clipboard items.
let clipboardItems = [];
// Assume `blob` is the blob representation of `kitten.webp`.
clipboardItems.push(
new ClipboardItem({
[blob.type]: blob,
})
);
await navigator.clipboard.write(clipboardItems);
console.log("Image copied, text ignored.");
} catch (err) {
console.error(err.name, err.message);
}
});
Pour l'événement copy
:
Pour ClipboardItem
:
Coller: lecture des données du presse-papiers
readText()
Pour lire le texte du presse-papiers, appelez navigator.clipboard.readText()
et patientez
pour la promesse renvoyée:
async function getClipboardContents() {
try {
const text = await navigator.clipboard.readText();
console.log('Pasted content: ', text);
} catch (err) {
console.error('Failed to read clipboard contents: ', err);
}
}
read()
La méthode navigator.clipboard.read()
est également asynchrone et renvoie une
prometteurs. Pour lire une image du presse-papiers, obtenez la liste des
ClipboardItem
d'objets, puis d'itérer dessus.
Chaque ClipboardItem
peut contenir son contenu de différents types. Vous devez donc
itérer sur la liste des types, encore une fois à l'aide d'une boucle for...of
. Pour chaque type,
appelez la méthode getType()
avec le type actuel comme argument pour obtenir le
blob correspondant. Comme précédemment, ce code n'est pas lié à des images
travailler avec d'autres
types de fichiers futurs.
async function getClipboardContents() {
try {
const clipboardItems = await navigator.clipboard.read();
for (const clipboardItem of clipboardItems) {
for (const type of clipboardItem.types) {
const blob = await clipboardItem.getType(type);
console.log(URL.createObjectURL(blob));
}
}
} catch (err) {
console.error(err.name, err.message);
}
}
Travailler avec des fichiers collés
Il est utile pour les utilisateurs de pouvoir utiliser les raccourcis clavier du presse-papiers, tels que ctrl+c et ctrl+v. Chromium affiche les fichiers en lecture seule dans le presse-papiers, comme indiqué ci-dessous. Cette action se déclenche lorsque l'utilisateur clique sur le raccourci de collage par défaut du système d'exploitation. ou lorsque l'utilisateur clique sur Modifier, puis sur Coller dans la barre de menu du navigateur. Aucun autre code de plomberie n'est nécessaire.
document.addEventListener("paste", async e => {
e.preventDefault();
if (!e.clipboardData.files.length) {
return;
}
const file = e.clipboardData.files[0];
// Read the file's contents, assuming it's a text file.
// There is no way to write back to it.
console.log(await file.text());
});
L'événement de collage
Comme indiqué précédemment, des événements sont prévus pour fonctionner avec l'API Clipboard.
Toutefois, pour l'instant, vous pouvez utiliser l'événement paste
existant. Il fonctionne parfaitement avec le nouveau
des méthodes asynchrones pour lire le texte du presse-papiers. Comme pour l'événement copy
,
oublie d'appeler preventDefault()
.
document.addEventListener('paste', async (e) => {
e.preventDefault();
const text = await navigator.clipboard.readText();
console.log('Pasted text: ', text);
});
Gérer plusieurs types MIME
La plupart des implémentations placent plusieurs formats de données dans le presse-papiers pour une seule coupe. ou de copie. Il y a deux raisons à cela: en tant que développeur d'applications, aucun moyen de connaître les fonctionnalités de l'application dans lesquelles un utilisateur souhaite copier du texte ou des images, et de nombreuses applications prennent en charge le collage de données structurées en texte brut. Il s'agit généralement présentée aux utilisateurs avec un élément de menu Édition, nommé Coller et style de correspondance ou Coller sans mise en forme.
L'exemple suivant montre comment procéder. Cet exemple utilise fetch()
pour obtenir
des données d'image, mais aussi
<canvas>
ou l'API File System Access.
async function copy() {
const image = await fetch('kitten.png').then(response => response.blob());
const text = new Blob(['Cute sleeping kitten'], {type: 'text/plain'});
const item = new ClipboardItem({
'text/plain': text,
'image/png': image
});
await navigator.clipboard.write([item]);
}
Sécurité et autorisations
L'accès au presse-papiers a toujours posé un problème de sécurité pour les navigateurs. Sans
les autorisations appropriées, une page peut copier silencieusement toutes sortes de contenus malveillants
dans le presse-papiers d'un utilisateur
qui produisait des résultats catastrophiques une fois collés.
Imaginez une page Web qui copie silencieusement rm -rf /
ou un
image d'une bombe de décompression
dans votre presse-papiers.
![Invite du navigateur demandant à l'utilisateur l'autorisation d'accéder au presse-papiers.](https://web.dev/static/articles/async-clipboard/image/browser-prompt-asking-us-40b4cf43bf01.png?authuser=5&hl=fr)
Accorder aux pages Web un accès en lecture illimité au presse-papiers est encore plus gênantes. Les utilisateurs copient régulièrement des informations sensibles telles que des mots de passe et vos informations personnelles dans le presse-papiers, qui peut ensuite être lu par n'importe quelle page les connaissances de l'utilisateur.
Comme c'est le cas avec de nombreuses nouvelles API, l'API Clipboard n'est compatible qu'avec les pages diffusées HTTPS Pour éviter les abus, l'accès au presse-papiers n'est autorisé que lorsqu'une page est l'onglet actif. Les pages des onglets actifs peuvent écrire dans le presse-papiers sans demande l'autorisation, mais la lecture à partir du presse-papiers nécessite toujours l'autorisation.
Les autorisations de copier-coller ont été ajoutées à
API Permissions :
L'autorisation clipboard-write
est accordée automatiquement aux pages lorsqu'elles sont
l'onglet actif. L'autorisation clipboard-read
doit être demandée, ce que vous pouvez
en essayant de lire les données
du presse-papiers. Le code ci-dessous illustre ce dernier:
const queryOpts = { name: 'clipboard-read', allowWithoutGesture: false };
const permissionStatus = await navigator.permissions.query(queryOpts);
// Will be 'granted', 'denied' or 'prompt':
console.log(permissionStatus.state);
// Listen for changes to the permission state
permissionStatus.onchange = () => {
console.log(permissionStatus.state);
};
Vous pouvez également contrôler si un geste de l'utilisateur est nécessaire pour appeler
en les collant à l'aide de l'option allowWithoutGesture
. La valeur par défaut pour cette valeur
varie selon le navigateur. Vous devez donc toujours l'inclure.
C'est là que la nature asynchrone de l'API Clipboard s'avère vraiment pratique: toute tentative de lecture ou d'écriture des données du presse-papiers invite automatiquement l'utilisateur à saisir si elle ne l'a pas déjà été. L'API étant basée sur des promesses, cela est totalement transparent. Si un utilisateur refuse l'autorisation d'utiliser le presse-papiers, la promesse de rejeter afin que la page puisse réagir de manière appropriée.
Les navigateurs n'autorisent l'accès au presse-papiers que lorsqu'une page est dans l'onglet actif.
vous constaterez que certains des exemples présentés ici ne s'exécutent pas s'ils sont collés directement dans
la console du navigateur, car les outils
de développement eux-mêmes constituent l'onglet actif. Une astuce consiste
à différer
accéder au presse-papiers avec setTimeout()
, puis cliquer rapidement dans la page pour
avant que les fonctions ne soient appelées:
setTimeout(async () => {
const text = await navigator.clipboard.readText();
console.log(text);
}, 2000);
Intégration des règles d'autorisation
Pour utiliser l'API dans des cadres iFrame, vous devez l'activer avec
Règles relatives aux autorisations
qui définit un mécanisme permettant d'activer et de
la désactivation de diverses API et fonctionnalités du navigateur. Concrètement, vous devez transmettre
clipboard-read
ou clipboard-write
, ou les deux, selon les besoins de votre application.
<iframe
src="index.html"
allow="clipboard-read; clipboard-write"
>
</iframe>
Détection de caractéristiques
Pour utiliser l'API Async Clipboard avec tous les navigateurs, testez
navigator.clipboard
et revenir aux méthodes précédentes. Par exemple, voici comment
vous pouvez implémenter le collage pour inclure d'autres navigateurs.
document.addEventListener('paste', async (e) => {
e.preventDefault();
let text;
if (navigator.clipboard) {
text = await navigator.clipboard.readText();
}
else {
text = e.clipboardData.getData('text/plain');
}
console.log('Got pasted text: ', text);
});
Ce n'est pas tout. Avant l'API Async Clipboard,
différentes implémentations de copier-coller dans les navigateurs Web. Dans la plupart des navigateurs,
les copier-coller du navigateur peuvent être déclenchés
document.execCommand('copy')
et document.execCommand('paste')
. Si le texte
à copier est une chaîne qui ne figure pas dans le DOM, elle doit être injectée dans le
DOM et sélectionnés:
button.addEventListener('click', (e) => {
const input = document.createElement('input');
input.style.display = 'none';
document.body.appendChild(input);
input.value = text;
input.focus();
input.select();
const result = document.execCommand('copy');
if (result === 'unsuccessful') {
console.error('Failed to copy text.');
}
input.remove();
});
Démonstrations
Vous pouvez jouer avec l'API Async Clipboard dans les démos ci-dessous. Sur Glitch, vous peut remixer la démo textuelle ou l'image de démonstration pour pour les tester.
Le premier exemple montre comment déplacer du texte dans le presse-papiers et en dehors.
Pour tester l'API avec des images, utilisez cette démonstration. N'oubliez pas que seuls les fichiers PNG sont acceptés et seulement dans certains navigateurs.
Liens associés
Remerciements
L'API Asynchronous Clipboard a été mise en œuvre par Darwin. Huang et Gary Kačmarčík Darwin a également fourni la démonstration. Merci à Kyarik et encore à Gary Kačmarčík pour pour revoir certaines parties de cet article.
Image héros de Markus Winkler sur Unsplash.