JS
() =>
JavaScript · Clipboard API · Copy & Paste · Browser APIs
JavaScript Clipboard API
Copy & Paste ohne execCommand

execCommand('copy') war ein Hack, der in modernen Browsern als veraltet markiert ist. Die Clipboard API liefert eine saubere, Promise-basierte Schnittstelle für das Lesen und Schreiben der Zwischenablage – inklusive Bild- und Rich-Text-Support sowie integriertem Berechtigungsmodell.

10 Min. Lesezeit navigator.clipboard · writeText · readText · ClipboardItem Chrome · Firefox · Safari · Edge

1. Das Problem mit execCommand

document.execCommand('copy') war jahrelang der einzige Weg, Text programmatisch in die Zwischenablage zu kopieren. Das Verfahren ist umständlich: Man muss ein unsichtbares Textarea-Element erstellen, Text einfügen, es in den DOM hängen, es selektieren, execCommand aufrufen und das Element wieder entfernen. Dabei kann execCommand auf einigen Browsern und in bestimmten Kontexten ohne Fehlermeldung scheitern – der Rückgabewert false signalisiert Fehler, liefert aber keinen Grund. Kein Promise, kein await, keine verlässliche Fehlerbehandlung.

Die Clipboard API löst all diese Probleme. Sie ist Promise-basiert, kann mit async/await verwendet werden, wirft bei Fehlern echte Exceptions und ist Teil einer offiziellen W3C-Spezifikation. Die alte execCommand-Methode ist in der WHATWG-Spezifikation als "legacy" markiert und wird in einigen Browsern schrittweise entfernt. Wer heute neue Clipboard-Funktionalität baut, sollte ausschließlich die Clipboard API über navigator.clipboard verwenden – und für ältere Browser einen gezielten Fallback bereithalten.

2. Die Clipboard API: Überblick und Verfügbarkeit

Die Clipboard API ist über das Objekt navigator.clipboard zugänglich. Sie stellt vier Kernmethoden bereit: writeText(text) schreibt einen String in die Zwischenablage, readText() liest den Textinhalt der Zwischenablage, write(items) schreibt beliebige MIME-Typen inklusive Bilder, und read() liest alle aktuellen Zwischenablageinhalte. Alle vier Methoden geben Promises zurück. Die Clipboard API ist in allen modernen Browsern verfügbar – Chrome ab Version 66, Firefox ab 63, Safari ab 13.1, Edge ab 79.

Ein wichtiges Sicherheitskonzept der Clipboard API: Sie funktioniert nur in sicheren Kontexten (HTTPS oder localhost). Auf HTTP-Seiten ist navigator.clipboard undefined. Außerdem unterscheidet die Spezifikation zwischen Schreib- und Lesezugriff: Schreiben (writeText, write) wird in vielen Browsern automatisch erlaubt, wenn der Code in einem User-Gesture-Handler (z.B. einem Click-Event) läuft. Lesen (readText, read) erfordert hingegen eine explizite Benutzererlaubnis – entweder durch den Browser-Permission-Dialog oder durch den Fokus des Browser-Fensters in Verbindung mit einem aktiven User-Gesture.


// Guard: Check if Clipboard API is available (requires HTTPS)
function isClipboardAvailable() {
  return (
    'clipboard' in navigator &&
    typeof navigator.clipboard.writeText === 'function'
  );
}

// Simple copy button — must be inside a user gesture handler
document.getElementById('copy-btn').addEventListener('click', async () => {
  if (!isClipboardAvailable()) {
    showToast('Clipboard not supported in this browser');
    return;
  }

  try {
    await navigator.clipboard.writeText('https://mironsoft.de');
    showToast('Link copied!');
  } catch (err) {
    // DOMException: NotAllowedError if permission denied
    console.error('Clipboard write failed:', err.name, err.message);
    showToast('Copy failed — please copy manually');
  }
});

3. Text in die Zwischenablage schreiben

navigator.clipboard.writeText(text) ist die einfachste Methode der Clipboard API. Sie gibt ein Promise zurück, das auflöst, wenn das Schreiben erfolgreich war, oder mit einem DOMException-Fehler ablehnt. Der häufigste Fehler ist NotAllowedError, der auftritt, wenn der Aufruf nicht innerhalb eines User-Gesture-Handlers stattfindet oder der Nutzer die Berechtigung verweigert hat. Das Promise erlaubt sauberes Error-Handling mit try/catch statt dem fragilen Boolean-Rückgabewert von execCommand.

Ein praxistaugliches Muster für die Clipboard API: Ein generischer Copy-Button, der ein data-clipboard-text-Attribut liest, den Text kopiert, visuelles Feedback gibt (z.B. Icon-Wechsel, Toast-Nachricht) und nach zwei Sekunden den ursprünglichen Zustand wiederherstellt. Dieses Muster lässt sich ohne JavaScript-Framework mit wenigen Zeilen Code implementieren und funktioniert für alle Copy-Buttons auf einer Seite, wenn man Event-Delegation verwendet.


// Generic copy button with visual feedback
document.addEventListener('click', async (event) => {
  const btn = event.target.closest('[data-clipboard-text]');
  if (!btn) return;

  const text = btn.dataset.clipboardText;
  const originalLabel = btn.textContent;

  try {
    await navigator.clipboard.writeText(text);

    // Visual feedback — restore after 2 seconds
    btn.textContent = 'Copied!';
    btn.setAttribute('aria-label', 'Copied to clipboard');
    btn.disabled = true;

    setTimeout(() => {
      btn.textContent = originalLabel;
      btn.removeAttribute('aria-label');
      btn.disabled = false;
    }, 2000);
  } catch {
    btn.textContent = 'Failed';
    setTimeout(() => { btn.textContent = originalLabel; }, 2000);
  }
});

// Usage in HTML:
// <button data-clipboard-text="npm install mironsoft-ui">Copy</button>

4. Text aus der Zwischenablage lesen

navigator.clipboard.readText() liest den aktuellen Textinhalt der Zwischenablage und gibt ihn als Promise zurück. Diese Methode erfordert die Berechtigung clipboard-read und löst einen NotAllowedError aus, wenn sie verweigert wird. In der Praxis öffnet der Browser bei der ersten Verwendung einen Berechtigungsdialog. Safari verhält sich hier abweichend: Es erlaubt readText() nur dann, wenn der Fokus auf dem Browser-Tab liegt und ein aktiver User-Gesture vorhanden ist – ohne expliziten Permission-Dialog.

Typische Anwendungsfälle für readText() mit der Clipboard API sind Paste-Buttons in Code-Editoren, die den Inhalt der Zwischenablage in ein Eingabefeld einfügen, oder Analyse-Tools, die einen eingefügten Text direkt verarbeiten. Für einfaches Einfügen in ein fokussiertes Eingabefeld ist das paste-Event mit event.clipboardData.getData('text') oft die bessere Wahl, weil es keine explizite Berechtigung erfordert und breiter kompatibel ist.

5. Berechtigungen mit der Permissions API

Die Clipboard API ist eng mit der Permissions API verknüpft. Mit navigator.permissions.query({ name: 'clipboard-read' }) lässt sich der aktuelle Berechtigungsstatus abfragen, ohne den Permission-Dialog auszulösen. Mögliche Werte sind 'granted', 'denied' und 'prompt'. Das erlaubt es, UI-Elemente entsprechend anzupassen: Ist die Berechtigung bereits erteilt, kann ein Paste-Button sofort aktiv sein. Ist sie verweigert, zeigt man einen Hinweis, wie der Nutzer sie erteilen kann. Ist sie 'prompt', zeigt man den Button und löst den Dialog beim Klick aus.

Für clipboard-write ist der Status in Chrome immer 'granted', solange ein aktiver User-Gesture vorliegt – das permissions.query für clipboard-write ist daher in der Praxis weniger nützlich als für clipboard-read. Firefox unterstützt permissions.query für Clipboard-Berechtigungen noch nicht vollständig, Safari ebenfalls nicht. Für produktionstaugliche Clipboard API-Implementierungen empfiehlt sich daher eine defensive Strategie: immer mit try/catch arbeiten und nie davon ausgehen, dass eine Berechtigung erteilt ist.


// Check clipboard-read permission before showing paste button
async function checkClipboardReadPermission() {
  if (!('permissions' in navigator)) return 'unknown';

  try {
    const status = await navigator.permissions.query({ name: 'clipboard-read' });
    // status.state: 'granted' | 'denied' | 'prompt'
    return status.state;
  } catch {
    // Firefox: clipboard-read not recognized in permissions API
    return 'unknown';
  }
}

// Adaptive paste button initialization
async function initPasteButton(btn) {
  const permission = await checkClipboardReadPermission();

  if (permission === 'denied') {
    btn.disabled = true;
    btn.title = 'Clipboard access denied. Enable it in browser settings.';
    return;
  }

  btn.addEventListener('click', async () => {
    try {
      const text = await navigator.clipboard.readText();
      document.getElementById('input-field').value = text;
    } catch (err) {
      if (err.name === 'NotAllowedError') {
        showPermissionHint();
      }
    }
  });
}

6. ClipboardItem: Bilder und Rich Text

Mit navigator.clipboard.write([new ClipboardItem({...})]) erlaubt die Clipboard API das Schreiben beliebiger MIME-Typen. Das klassische Anwendungsbeispiel ist das Kopieren eines Canvas-Inhalts als PNG-Bild. Der Ablauf: Canvas-Inhalt mit canvas.toBlob() in einen Blob konvertieren, den Blob in ein ClipboardItem verpacken und das Item mit navigator.clipboard.write() in die Zwischenablage schreiben. Der Nutzer kann das Bild danach direkt in andere Anwendungen (Bildeditor, E-Mail-Client, Word) einfügen.

Safari hat hier eine Besonderheit: Es unterstützt ClipboardItem erst ab Safari 13.1 und erwartet, dass der Blob-Generator ein Promise ist, das synchron während des User-Gestures initiiert wird. Das bedeutet, man kann den Blob nicht nach einem await erstellen und dann ClipboardItem befüllen – das User-Gesture-Fenster ist zu diesem Zeitpunkt in Safari bereits geschlossen. Die Safari-kompatible Lösung übergibt dem ClipboardItem direkt ein Promise für den Blob. Die Clipboard API-Spezifikation erlaubt das seit 2021 explizit.


// Copy canvas content as PNG image to clipboard
async function copyCanvasAsImage(canvas) {
  if (!('ClipboardItem' in window)) {
    throw new Error('ClipboardItem not supported — update your browser');
  }

  // Safari-compatible: pass a Promise directly to ClipboardItem
  const blobPromise = new Promise((resolve, reject) => {
    canvas.toBlob((blob) => {
      if (blob) resolve(blob);
      else reject(new Error('Canvas toBlob failed'));
    }, 'image/png');
  });

  await navigator.clipboard.write([
    new ClipboardItem({ 'image/png': blobPromise }),
  ]);
}

// Copy both plain text and HTML to clipboard simultaneously
async function copyRichText(html, plainText) {
  const htmlBlob = new Blob([html], { type: 'text/html' });
  const textBlob = new Blob([plainText], { type: 'text/plain' });

  await navigator.clipboard.write([
    new ClipboardItem({
      'text/html': htmlBlob,
      'text/plain': textBlob,
    }),
  ]);
}

// Usage
document.getElementById('copy-image-btn').addEventListener('click', async () => {
  const canvas = document.querySelector('canvas');
  try {
    await copyCanvasAsImage(canvas);
    showToast('Image copied to clipboard!');
  } catch (err) {
    console.error(err);
    showToast('Copy failed: ' + err.message);
  }
});

7. Clipboard-Events: copy, cut und paste

Neben der asynchronen Clipboard API über navigator.clipboard gibt es die synchronen Clipboard-Events copy, cut und paste. Diese Events werden ausgelöst, wenn der Nutzer die entsprechenden Tastenkombinationen oder Kontextmenüaktionen ausführt. Im Event-Handler hat man über event.clipboardData Zugriff auf die Zwischenablage und kann deren Inhalt lesen oder modifizieren, bevor der Browser seine Standardaktion ausführt. event.preventDefault() stoppt die Standardaktion und gibt einem vollständige Kontrolle.

Das paste-Event mit event.clipboardData.getData('text/plain') ist breiter kompatibel als navigator.clipboard.readText() und erfordert keine explizite Berechtigungserteilung. Es funktioniert auch in älteren Browsern und in Firefox ohne den clipboard-read-Permission-Dialog. Das macht den Event-basierten Ansatz zur bevorzugten Methode, wenn man nur auf Paste-Aktionen des Nutzers reagieren will – ohne aktiv die Zwischenablage auszulesen. Die asynchrone Clipboard API ist hingegen die richtige Wahl, wenn man programmatisch zu einem beliebigen Zeitpunkt schreiben oder lesen muss.

8. Fallbacks für ältere Browser und Sonderfälle

Auch wenn die Clipboard API in allen modernen Browsern unterstützt wird, gibt es Szenarien, in denen Fallbacks notwendig sind: HTTP-Seiten (kein HTTPS), ältere Browser-Versionen, Browser-Erweiterungen die den Clipboard-Zugriff blockieren, und headless Browser in Tests. Ein robuster Fallback für writeText erstellt ein textarea-Element, setzt seinen Wert, hängt es versteckt an den Body, selektiert den Inhalt und ruft document.execCommand('copy') auf. Danach wird das Element entfernt.

Ein weiterer Sonderfall: In Electron-Apps und bestimmten WebView-Kontexten ist navigator.clipboard zwar vorhanden, aber die Berechtigungen verhalten sich anders als im Browser. Electron stellt eigene APIs bereit (clipboard aus dem electron-Modul), die zuverlässiger sind. Für eine produktionstaugliche Bibliothek rund um die Clipboard API empfiehlt sich folgende Hierarchie: zuerst navigator.clipboard.writeText() versuchen, bei Fehler auf execCommand-Fallback zurückfallen, bei erneutem Fehler den Nutzer auffordern, manuell zu kopieren.

Methode Browser-Support Berechtigung nötig HTTPS nötig
navigator.clipboard.writeText() Alle modernen Browser Nein (User-Gesture reicht) Ja
navigator.clipboard.readText() Alle modernen Browser Ja (clipboard-read) Ja
ClipboardItem (Bilder) Chrome, Edge, Safari 13.1+ Nein (User-Gesture reicht) Ja
clipboard-Events (paste) Alle Browser inkl. IE11 Nein Nein
execCommand('copy') Veraltet, bald entfernt Nein Nein

9. Clipboard API vs. execCommand im Vergleich

Der praktische Unterschied zwischen der modernen Clipboard API und execCommand wird besonders bei der Fehlerbehandlung sichtbar. execCommand('copy') gibt false zurück, wenn es fehlschlägt – ohne Fehlermeldung, ohne Fehlertyp, ohne Stack-Trace. Die Clipboard API wirft einen DOMException mit Namen (NotAllowedError, SecurityError) und Nachricht. Das macht Debugging und kontextsensitives Error-Handling möglich.

Ein weiterer wesentlicher Unterschied: execCommand funktioniert nur synchron im Kontext des aktiven Dokuments und erfordert ein selektiertes DOM-Element. Die Clipboard API ist dokumentunabhängig und kann jeden String oder Blob-Typ ohne DOM-Manipulation schreiben. Für die Zukunft ist der Entscheid klar: execCommand steht auf der Deprecation-Liste aller Browserhersteller, die Clipboard API wird aktiv weiterentwickelt. Wer heute Code schreibt, sollte ausschließlich auf die asynchrone Clipboard API setzen.

Mironsoft

Moderne Browser APIs, JavaScript-Entwicklung und Web-Standards

Veraltetes execCommand aus Ihrem Code entfernen?

Wir migrieren bestehende Copy-Paste-Funktionen auf die moderne Clipboard API – mit robuster Fehlerbehandlung, Browser-Fallbacks und vollständiger Unterstützung für Text, Bilder und Rich Text.

Code-Migration

execCommand-Aufrufe identifizieren und durch Clipboard API ersetzen

Browser-Kompatibilität

Safari-Sonderfälle, Firefox-Permissions und robuste Fallback-Strategien

Rich-Text-Support

ClipboardItem für Bilder und HTML — auch Canvas-Export in die Zwischenablage

10. Zusammenfassung

Die Clipboard API über navigator.clipboard ist der moderne, standardkonforme Ersatz für das veraltete document.execCommand('copy'). Sie bietet Promise-basierte Methoden für Text (writeText, readText) und beliebige MIME-Typen (write mit ClipboardItem), unterstützt echte Fehlerbehandlung mit try/catch und integriert sich sauber mit der Permissions API. Die wichtigsten Einschränkungen sind die HTTPS-Anforderung und die Berechtigungspflicht für readText() – beide lassen sich mit einem durchdachten Feature-Detection-Pattern und gezielten Fallbacks abfangen.

Für neue Projekte gilt: execCommand nie mehr verwenden. Für bestehende Codebases lohnt sich die Migration – insbesondere weil execCommand in Chrome und Firefox aktiv auf der Deprecation-Liste steht. Die Clipboard API ermöglicht außerdem Features, die mit execCommand nicht möglich waren: das Kopieren von Canvas-Inhalten als Bilder, das gleichzeitige Schreiben mehrerer MIME-Typen und das Reagieren auf Berechtigungsänderungen des Nutzers in Echtzeit.

Clipboard API — Das Wichtigste auf einen Blick

Text schreiben

await navigator.clipboard.writeText(text) – in User-Gesture-Handler aufrufen. Promise-basiert, wirft echte Exceptions. HTTPS erforderlich.

Text lesen

await navigator.clipboard.readText() – erfordert clipboard-read-Berechtigung. Alternative: paste-Event ohne Berechtigung.

Bilder und Rich Text

ClipboardItem mit Blob – Safari erwartet Promise direkt im Konstruktor. Firefox-Support für ClipboardItem ist eingeschränkt.

Fallback

Feature-Detection: 'clipboard' in navigator. Fallback auf execCommand für HTTP-Seiten. Bei Fehler: Nutzer zum manuellen Kopieren auffordern.

11. FAQ: JavaScript Clipboard API

1Warum ist execCommand veraltet?
Synchron, kein verlässliches Error-Handling, erfordert DOM-Manipulation. In WHATWG als legacy markiert, wird von Browsern schrittweise entfernt.
2Warum nur auf HTTPS?
Clipboard API ist Teil der Secure Context API. Auf HTTP wäre Clipboard-Auslesen über Man-in-the-Middle denkbar. Localhost ohne HTTPS ist erlaubt.
3Brauche ich für writeText() eine Berechtigung?
Nein, ein aktiver User-Gesture reicht. clipboard-write ist standardmäßig erteilt in Click-Handlern.
4Wie kopiere ich ein Bild?
ClipboardItem mit PNG-Blob aus canvas.toBlob(). Safari erwartet das Promise direkt im Konstruktor.
5Text ohne clipboard-read lesen?
paste-Event: event.clipboardData.getData('text/plain'). Keine Berechtigung nötig, funktioniert in allen Browsern.
6Berechtigung prüfen?
navigator.permissions.query({ name: 'clipboard-read' }) – gibt 'granted', 'denied' oder 'prompt' zurück. Firefox/Safari unvollständig.
7HTML-Text kopieren?
clipboard.write() mit ClipboardItem für text/html und text/plain gleichzeitig. Empfänger wählt bevorzugten MIME-Typ.
8Was tun wenn Clipboard API fehlt?
'clipboard' in navigator prüfen. Fallback auf execCommand-textarea-Trick. Bei Fehler Nutzer zum manuellen Kopieren auffordern.
9Clipboard API in iframes?
Nur mit allow="clipboard-read clipboard-write" auf dem iframe-Element. Ohne Freigabe schlagen Clipboard-Operationen fehl.
10Clipboard in Tests mocken?
Playwright: context.grantPermissions(['clipboard-read', 'clipboard-write']). In Unit-Tests navigator.clipboard mit vi.spyOn oder Jest-Mock ersetzen.