Clipboard API in der Praxis: Browser-Zwischenablage Lesen/Schreiben und Sicherheitsrichtlinien

技术架构

Clipboard API: Der moderne Standard für Zwischenablage-Interaktion

Browser-Zwischenablage-Operationen haben sich von document.execCommand zur Async Clipboard API weiterentwickelt:

Aspekt execCommand('copy') Async Clipboard API
Standard Veraltet W3C-empfohlen
Asynchron Nein (synchrone Blockierung) Ja (Promise-basiert)
Berechtigungen Implizit (Benutzergeste) Explizite Permissions API
Rich Text Nicht unterstützt Unterstützt (ClipboardItem)
Benutzerdefinierte Formate Nicht unterstützt Unterstützt (Web-Custom-Formate)
Bild-Lesen/Schreiben Nicht unterstützt Unterstützt
Zuverlässigkeit Hängt von Auswahl ab Direkter Zwischenablage-Zugriff

Deep Dive in die Kern-API

Clipboard-API-Schnittstellen

interface Clipboard extends EventTarget {
  read(): Promise<ClipboardItem[]>;
  readText(): Promise<string>;
  write(data: ClipboardItem[]): Promise<void>;
  writeText(text: string): Promise<void>;
}

interface ClipboardItem {
  readonly types: string[];
  getType(type: string): Promise<Blob>;
}

Text-Lesen/Schreiben

async function copyText(text: string) {
  try {
    await navigator.clipboard.writeText(text);
    showToast('In Zwischenablage kopiert');
  } catch (err) {
    console.error('Kopieren fehlgeschlagen:', err);
    fallbackCopyText(text);
  }
}

async function pasteText(): Promise<string> {
  try {
    const text = await navigator.clipboard.readText();
    return text;
  } catch (err) {
    console.error('Lesen fehlgeschlagen:', err);
    return '';
  }
}

Rich-Text- und Multi-Format-Schreiben

ClipboardItem Multi-Format

async function copyRichText(html: string, plainText: string) {
  const clipboardItem = new ClipboardItem({
    'text/html': new Blob([html], { type: 'text/html' }),
    'text/plain': new Blob([plainText], { type: 'text/plain' }),
  });
  await navigator.clipboard.write([clipboardItem]);
}

copyRichText(
  '<strong>Wichtig</strong>: Einreichen bis <em>Freitag</em>',
  'Wichtig: Einreichen bis Freitag'
);

Bild kopieren

async function copyImage(pngBlob: Blob) {
  await navigator.clipboard.write([
    new ClipboardItem({ 'image/png': pngBlob })
  ]);
}

async function copyCanvas(canvas: HTMLCanvasElement) {
  const blob = await new Promise<Blob>((resolve) => {
    canvas.toBlob((b) => resolve(b!), 'image/png');
  });
  await navigator.clipboard.write([
    new ClipboardItem({ 'image/png': blob })
  ]);
}

Bild einfügen

document.addEventListener('paste', async (e) => {
  const items = e.clipboardData?.items;
  if (!items) return;

  for (const item of items) {
    if (item.type.startsWith('image/')) {
      const blob = item.getAsFile()!;
      const url = URL.createObjectURL(blob);
      const img = document.createElement('img');
      img.src = url;
      document.body.appendChild(img);
    }
  }
});

Benutzerdefinierte MIME-Typen (Web-Custom-Formate)

async function copyCustomData(data: object) {
  const json = JSON.stringify(data);
  await navigator.clipboard.write([
    new ClipboardItem({
      'web application/json': new Blob([json], { type: 'application/json' }),
      'text/plain': new Blob([json], { type: 'text/plain' }),
    })
  ]);
}

async function pasteCustomData(): Promise<object | null> {
  const items = await navigator.clipboard.read();
  for (const item of items) {
    if (item.types.includes('web application/json')) {
      const blob = await item.getType('web application/json');
      const text = await blob.text();
      return JSON.parse(text);
    }
  }
  return null;
}

Das web -Präfix ist eine Sicherheitsanforderung für benutzerdefinierte Formate und verhindert, dass Seiten native Zwischenablage-Formate lesen.


Berechtigungen und Sicherheitsrichtlinien

Permissions API

async function checkClipboardPermission(): Promise<boolean> {
  try {
    const result = await navigator.permissions.query({
      name: 'clipboard-read' as PermissionName
    });
    return result.state === 'granted';
  } catch {
    return false;
  }
}

Sicherheitsbeschränkungen

Operation Voraussetzung Hinweise
writeText Benutzeraktivierung Klick/Tasten-Geste erforderlich
write Benutzeraktivierung Wie oben
readText Benutzeraktivierung + Berechtigung Benutzerautorisierungsaufforderung
read Benutzeraktivierung + Berechtigung Benutzerautorisierungsaufforderung
Fokussiertes Fenster Dokument muss fokussiert sein Aufrufe in unfokussiertem Fenster werden abgelehnt

Benutzeraktivierungsprüfung

button.addEventListener('click', async () => {
  // ✅ Innerhalb des Klick-Events — Benutzeraktivierung vorhanden
  await navigator.clipboard.writeText('kopiert!');
});

setTimeout(async () => {
  await navigator.clipboard.writeText('fehlschlägt'); // ❌ DOMException
}, 1000);

Fallback: execCommand

function fallbackCopyText(text: string): boolean {
  const textarea = document.createElement('textarea');
  textarea.value = text;
  textarea.style.position = 'fixed';
  textarea.style.left = '-9999px';
  document.body.appendChild(textarea);
  textarea.select();

  try {
    const success = document.execCommand('copy');
    document.body.removeChild(textarea);
    return success;
  } catch {
    document.body.removeChild(textarea);
    return false;
  }
}

async function safeCopy(text: string) {
  if (navigator.clipboard?.writeText) {
    try {
      await navigator.clipboard.writeText(text);
      return true;
    } catch {}
  }
  return fallbackCopyText(text);
}

Tiefe Paste-Event-Behandlung

Einfügeinhalt abfangen und transformieren

editor.addEventListener('paste', (e) => {
  e.preventDefault();
  const clipboardData = e.clipboardData!;

  const html = clipboardData.getData('text/html');
  if (html) {
    const sanitized = sanitizeHTML(html);
    insertHTML(sanitized);
    return;
  }

  const text = clipboardData.getData('text/plain');
  if (text) {
    const linked = text.replace(
      /https?:\/\/\S+/g,
      (url) => `<a href="${url}">${url}</a>`
    );
    insertHTML(linked);
  }
});

Zwischenablage-Datentyperkennung

editor.addEventListener('paste', (e) => {
  const data = e.clipboardData!;
  console.log('Typen:', [...data.types]);

  const priorityOrder = [
    'text/html',
    'application/json',
    'text/plain',
    'image/png',
  ];

  for (const type of priorityOrder) {
    if (data.types.includes(type)) {
      const content = data.getData(type);
      console.log(`Einfügen ${type}:`, content.slice(0, 100));
      break;
    }
  }
});

Praxis: Kopieren-Button-Komponente

class CopyButton {
  private timeoutId: number | null = null;

  constructor(private button: HTMLButtonElement, private getText: () => string) {
    button.addEventListener('click', () => this.handleCopy());
  }

  private async handleCopy() {
    const text = this.getText();
    const success = await safeCopy(text);

    if (success) {
      this.showFeedback('Kopiert!');
    } else {
      this.showFeedback('Fehlgeschlagen');
    }
  }

  private showFeedback(message: string) {
    const original = this.button.textContent;
    this.button.textContent = message;
    this.button.disabled = true;

    if (this.timeoutId) clearTimeout(this.timeoutId);
    this.timeoutId = window.setTimeout(() => {
      this.button.textContent = original;
      this.button.disabled = false;
    }, 2000);
  }
}

Browser-Kompatibilität

API Chrome Firefox Safari Edge
writeText 66+ 63+ 13.1+ 79+
readText 66+ 63+ 13.1+ 79+
write (ClipboardItem) 76+ 87+ 13.1+ 79+
read 76+ 87+ 13.1+ 79+
Web Custom Formats 104+ Nein Nein 104+
Bild-Zwischenablage 76+ 87+ 13.1+ 79+

Best Practices

  1. Immer Fallback bereitstellen: execCommand verwenden, wenn Clipboard API nicht verfügbar ist
  2. Innerhalb der Benutzeraktivierung aufrufen: Kopieren muss in Klick/Tasten-Handlern ausgelöst werden
  3. Immer Feedback anzeigen: Visuelles Feedback für Erfolg und Misserfolg
  4. Mit Vorsicht lesen: read() betrifft die Privatsphäre — nur bei Bedarf anfordern
  5. Einfügeinhalt bereinigen: HTML-Einfügen muss für XSS-Risiken gefiltert werden

Zusammenfassung

Die Async Clipboard API ist der Standardansatz für moderne Browser-Zwischenablage-Operationen und unterstützt Text-, Rich-Text-, Bild- und benutzerdefiniertes Format-Lesen/Schreiben. Zuverlässiger, sicherer und leistungsfähiger als execCommand, erfordert jedoch Beachtung der Benutzeraktivierung und Berechtigungsmodelle.

Verwenden Sie Clipboard Viewer zum Debuggen des Zwischenablage-Inhalts, Text Ersetzen für die Stapelverarbeitung von Einfügevorgängen und Groß-/Kleinschreibung zum Normalisieren von Zwischenablage-Text.

Probiere diese browser-lokalen Tools aus — keine Registrierung erforderlich →

#Clipboard API#剪贴板#异步API#浏览器安全#用户交互