Blockierung des Zugriffs auf die Zwischenablage aufheben

Sichererer Zugriff auf die Zwischenablage für Texte und Bilder

Der Zugriff auf die Systemzwischenablage erfolgte üblicherweise über document.execCommand() für Interaktionen in der Zwischenablage. Diese Methode zum Ausschneiden und Einfügen war zwar weithin unterstützt, hatte aber einen Nachteil: Der Zugriff auf die Zwischenablage war synchron und konnte nur lesen und schreiben im DOM.

Das ist für kleine Textmengen in Ordnung, aber es gibt viele Fälle, in denen das Blockieren der Seite für die Zwischenablageübertragung nicht optimal ist. Zeitaufwendige Bereinigungen oder Möglicherweise ist eine Bilddecodierung erforderlich, bevor Inhalte sicher eingefügt werden können. Der Browser muss möglicherweise verknüpfte Ressourcen aus einem eingefügten Dokument laden oder inline einfügen. Das würde die Seite während des Wartens auf dem Laufwerk oder Netzwerk blockieren. Berechtigungen hinzufügen Der Browser muss die Seite während der Anfrage blockieren. Zugriff auf die Zwischenablage. Gleichzeitig werden die Berechtigungen document.execCommand() für Interaktionen in der Zwischenablage sind lose definiert und variieren. zwischen Browsern.

Die Async Clipboard API behebt diese Probleme und bietet ein klar definiertes Berechtigungsmodell, das die Seite nicht blockiert. Die Async Clipboard API ist auf die Verarbeitung von Text und Bildern beschränkt. in den meisten Browsern. Die Unterstützung variiert jedoch. Lesen Sie sich die Übersicht zur Browserkompatibilität für jeden der folgenden Abschnitte sorgfältig durch.

Kopieren: Daten in die Zwischenablage schreiben

writeText()

Wenn Sie Text in die Zwischenablage kopieren möchten, drücken Sie writeText(). Da diese API asynchron ist, gibt die Funktion writeText() ein Promise zurück, das die werden abgelehnt, je nachdem, ob der übergebene Text kopiert wurde:

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);
  }
}

Unterstützte Browser

  • Chrome: 66 <ph type="x-smartling-placeholder">
  • Edge: 79. <ph type="x-smartling-placeholder">
  • Firefox: 63. <ph type="x-smartling-placeholder">
  • Safari: 13.1.

Quelle

write()

writeText() ist eigentlich nur eine praktische Methode für die generische write()-Methode, mit der Sie auch Bilder in die Zwischenablage kopieren können. Wie bei writeText() gibt es auch ist asynchron und gibt ein Versprechen zurück.

Wenn Sie ein Bild in die Zwischenablage kopieren möchten, benötigen Sie das Bild als blob. Eine Möglichkeit, fordern Sie das Image mithilfe von fetch() von einem Server an und rufen Sie dann blob() im Antwort.

Für einen Nutzer ist es möglicherweise nicht wünschenswert oder möglich, ein Bild vom Server anzufordern. aus verschiedenen Gründen. Glücklicherweise können Sie das Bild auch auf einer Canvas-Leinwand zeichnen und die Methode toBlob() der Canvas-Leinwand aufrufen.

Übergeben Sie als Nächstes ein Array von ClipboardItem-Objekten als Parameter an die write()-Methode. Derzeit können Sie nur ein Bild gleichzeitig übergeben. Wir hoffen aber, in Zukunft auch mehrere Bilder unterstützen zu können. ClipboardItem nimmt ein Objekt mit dem MIME-Typ des Bilds als Schlüssel und dem Blob als Wert an. Für Blob Objekte, die aus fetch() oder canvas.toBlob() abgerufen wurden, dem Attribut blob.type enthält automatisch den richtigen MIME-Typ für ein Bild.

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);
}

Alternativ können Sie ein Versprechen an das ClipboardItem-Objekt schreiben. Für dieses Muster müssen Sie den MIME-Typ der Daten im Voraus kennen.

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);
}

Unterstützte Browser

  • Chrome: 76.
  • Edge: 79.
  • Firefox: 127.
  • Safari: 13.1.

Quelle

Das Kopierereignis

Wenn ein Nutzer eine Zwischenablage-Kopie initiiert und preventDefault() nicht aufruft, enthält das copy-Ereignis die Eigenschaft clipboardData mit den Elementen bereits im richtigen Format. Wenn du deine eigene Logik implementieren möchtest, musst du preventDefault() aufrufen, um das Standardverhalten zu verhindern und deine eigene Implementierung zu verwenden. In diesem Fall ist clipboardData leer. Angenommen, es gibt eine Seite mit Text und einem Bild. Wenn der Nutzer den gesamten Text auswählt und eine Zwischenablage-Kopie startet, sollte Ihre benutzerdefinierte Lösung den Text verwerfen und nur das Bild kopieren. Das geht so, wie im Codebeispiel unten gezeigt. In diesem Beispiel wird nicht beschrieben, wie Sie auf ältere APIs zurückgreifen, wenn die Clipboard API nicht unterstützt wird.

<!-- 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);
  }
});

Für das copy-Ereignis:

Unterstützte Browser

  • Chrome: 1. <ph type="x-smartling-placeholder">
  • Edge: 12.
  • Firefox: 22. <ph type="x-smartling-placeholder">
  • Safari: 3. <ph type="x-smartling-placeholder">

Quelle

Für ClipboardItem:

Unterstützte Browser

  • Chrome: 76.
  • Edge: 79. <ph type="x-smartling-placeholder">
  • Firefox: 127. <ph type="x-smartling-placeholder">
  • Safari: 13.1. <ph type="x-smartling-placeholder">

Quelle

Einfügen: Daten aus der Zwischenablage lesen

readText()

Wenn du Text aus der Zwischenablage lesen möchtest, rufe navigator.clipboard.readText() auf und warte bis das zurückgegebene Promise aufgelöst wird:

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);
  }
}

Unterstützte Browser

  • Chrome: 66
  • Edge: 79.
  • Firefox: 125. <ph type="x-smartling-placeholder">
  • Safari: 13.1.

Quelle

read()

Die Methode navigator.clipboard.read() ist ebenfalls asynchron und gibt ein Versprechen zurück. Um ein Bild aus der Zwischenablage zu lesen, rufen Sie eine Liste mit ClipboardItem und dann iterieren.

Jede ClipboardItem kann ihren Inhalt in verschiedenen Typen enthalten. Deshalb müssen Sie iterieren Sie über die Liste der Typen, wieder mit einer for...of-Schleife. Rufen Sie für jeden Typ die Methode getType() mit dem aktuellen Typ als Argument auf, um den entsprechenden Blob abzurufen. Wie zuvor ist dieser Code nicht an Bilder gebunden. zukünftige Dateitypen verwenden können.

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);
  }
}

Unterstützte Browser

  • Chrome: 76.
  • Edge: 79.
  • Firefox: 127. <ph type="x-smartling-placeholder">
  • Safari: 13.1. <ph type="x-smartling-placeholder">

Quelle

Mit eingefügten Dateien arbeiten

Es ist hilfreich für Nutzende, wenn sie Tastenkombinationen für die Zwischenablage verwenden können, z. B. Strg + C und Strg + V. Chromium zeigt schreibgeschützt Dateien in der Zwischenablage an, wie unten beschrieben. Diese Option wird ausgelöst, wenn der Nutzer die standardmäßige Tastenkombination zum Einfügen des Betriebssystems verwendet oder wenn der Nutzer in der Menüleiste des Browsers auf Bearbeiten und dann auf Einfügen klickt. Es ist kein weiterer Plumbing-Code erforderlich.

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());
});

Unterstützte Browser

  • Chrome: 3. <ph type="x-smartling-placeholder">
  • Edge: 12. <ph type="x-smartling-placeholder">
  • Firefox: 3.6 <ph type="x-smartling-placeholder">
  • Safari: 4.

Quelle

Das Ereignis „Einfügevorgang“

Wie bereits erwähnt, gibt es Pläne, Ereignisse einzuführen, die mit der Clipboard API funktionieren, aber vorerst können Sie das vorhandene paste-Ereignis verwenden. Funktioniert problemlos mit dem neuen asynchrone Methoden zum Lesen von Text in der Zwischenablage. Wie beim copy-Ereignis sollten Sie vergessen, preventDefault() anzurufen.

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  const text = await navigator.clipboard.readText();
  console.log('Pasted text: ', text);
});

Unterstützte Browser

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 22. <ph type="x-smartling-placeholder">
  • Safari: 3.

Quelle

Mehrere MIME-Typen verarbeiten

Bei den meisten Implementierungen werden für einen einzelnen Schnitt mehrere Datenformate in die Zwischenablage eingefügt. oder einen Kopiervorgang ausführen. Dafür gibt es zwei Gründe: Als App-Entwickler die Funktionen der App nicht kennen, in die Nutzende Text oder Bilder kopieren möchten, und viele Anwendungen unterstützen das Einfügen strukturierter Daten als Nur-Text. Das ist normalerweise Der Menüpunkt Bearbeiten mit einem Namen wie Einfügen und Übereinstimmungsstil oder Ohne Formatierung einfügen aus.

Das folgende Beispiel zeigt, wie das geht. In diesem Beispiel wird mit fetch() Folgendes ermittelt: Bilddaten stammen, kann aber auch <canvas> oder die File System Access API verwenden.

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]);
}

Sicherheit und Berechtigungen

Der Zugriff auf die Zwischenablage war schon immer ein Sicherheitsrisiko für Browser. Ohne Berechtigungen erteilt, könnte eine Seite unbemerkt alle Arten schädlicher Inhalte kopieren in die Zwischenablage einfügen, die nach dem Einfügen katastrophale Folgen haben würden. Angenommen, eine Webseite kopiert automatisch rm -rf / oder einen Dekomprimierungsbombenbild in die Zwischenablage.

Browseraufforderung, in der der Nutzer um die Berechtigung zur Zwischenablage gebeten wird
Die Berechtigungsaufforderung für die Clipboard API.

Das Gewähren von uneingeschränkten Lesezugriffs auf die Zwischenablage durch Webseiten ist noch problematischer. Nutzer kopieren routinemäßig vertrauliche Informationen wie Passwörter und persönlichen Daten in die Zwischenablage legen, die dann von jeder Seite gelesen werden können, Wissen der Nutzenden.

Wie viele neue APIs wird die Clipboard API nur für Seiten unterstützt, die über HTTPS ausgeliefert werden. Um Missbrauch zu verhindern, ist der Zugriff auf die Zwischenablage nur zulässig, wenn eine Seite der aktive Tab ist. Seiten auf aktiven Tabs können in die Zwischenablage schreiben, ohne Berechtigung anfordern. Zum Lesen aus der Zwischenablage ist jedoch immer eine Berechtigung.

Berechtigungen zum Kopieren und Einfügen wurden hinzugefügt. Permissions API verfügbar. Die Berechtigung clipboard-write wird Seiten automatisch gewährt, wenn auf dem aktiven Tab. Die Berechtigung clipboard-read muss angefordert werden. Sie können das tun, indem Sie versuchen, Daten aus der Zwischenablage zu lesen. Der folgende Code zeigt das letztere Beispiel:

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);
};

Sie können auch festlegen, ob eine Nutzergeste zum Ausschneiden oder mit der Option allowWithoutGesture einfügen. Der Standardwert für diesen Wert variiert je nach Browser. Sie sollten ihn daher immer angeben.

Hier kommt der asynchrone Charakter des Clipboard API besonders gut an: Beim Versuch, Daten aus der Zwischenablage zu lesen oder zu schreiben, wird der Nutzer automatisch aufgefordert, wenn sie nicht bereits gewährt wurde. Da die API versprechensbasiert ist, ist dies völlig transparent. Wenn ein Nutzer die Berechtigung für die Zwischenablage verweigert, wird das Versprechen abgelehnt, damit die Seite entsprechend reagieren kann.

Da Browser nur den Zugriff auf die Zwischenablage erlauben, wenn eine Seite der aktive Tab ist, werden Sie feststellen, dass einige der Beispiele nicht funktionieren, wenn sie direkt in die Konsole des Browsers, da die Entwicklertools der aktive Tab sind. Es gibt einen Trick: das Verschieben mit setTimeout() auf die Zwischenablage zugreifen und dann schnell in die Seite klicken, fokussieren Sie es, bevor die Funktionen aufgerufen werden:

setTimeout(async () => {
  const text = await navigator.clipboard.readText();
  console.log(text);
}, 2000);

Integration von Berechtigungsrichtlinien

Wenn Sie die API in Iframes verwenden möchten, müssen Sie sie mit der Berechtigungsrichtlinie aktivieren. Diese definiert einen Mechanismus, mit dem verschiedene Browserfunktionen und APIs selektiv aktiviert und deaktiviert werden können. Je nach den Anforderungen Ihrer App müssen Sie entweder clipboard-read oder clipboard-write oder beide übergeben.

<iframe
    src="index.html"
    allow="clipboard-read; clipboard-write"
>
</iframe>

Funktionserkennung

Wenn Sie die Async Clipboard API verwenden und gleichzeitig alle Browser unterstützen möchten, testen Sie auf navigator.clipboard und greifen Sie bei Bedarf auf frühere Methoden zurück. Hier sehen Sie zum Beispiel, können Sie das Einfügen auch für andere Browser implementieren.

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);
});

Das ist aber noch nicht alles. Vor der Async Clipboard API gab es in Webbrowsern verschiedene Implementierungen für das Kopieren und Einfügen. In den meisten Browsern können die Kopier- und Einfügefunktionen des Browsers mit document.execCommand('copy') und document.execCommand('paste') ausgelöst werden. Wenn der Text eine Zeichenfolge ist, die nicht im DOM vorhanden ist, muss in das DOM und ausgewählt:

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();
});

Demos

In den folgenden Demos können Sie die Async Clipboard API ausprobieren. Auf Glitch können Sie die Textdemo oder die Bilddemo remixen, um damit zu experimentieren.

Das erste Beispiel zeigt, wie Text in die Zwischenablage und daraus entfernt wird.

Verwenden Sie diese Demo, um die API mit Bildern auszuprobieren. Zur Erinnerung: Es werden nur PNGs unterstützt und das auch nur in einigen Browsern.

Danksagungen

Die Async Clipboard API wurde von Darwin Huang und Gary Kačmarčík implementiert. Darwin hat auch die Demo zur Verfügung gestellt. Vielen Dank an Kyarik und Gary Kačmarčík einige Teile dieses Artikels.

Hero-Image von Markus Winkler auf Unsplash