איך להדביק תמונות

הארי תאודולו
הארי תאודולו

הדרך המודרנית

באמצעות Async Clipboard API

כדי לקרוא תמונות מהלוח של המשתמש באופן פרוגרמטי, כלומר, אחרי לחיצה על לחצן, אפשר להשתמש בשיטה read() של Async Clipboard API. אם עדיין לא ניתנה הרשאת קריאה מלוח העריכה, הקריאה ל-navigator.clipboard.read() תישלח באמצעות הקריאה הראשונה ל-method.

const pasteButton = document.querySelector('#paste-button');

pasteButton.addEventListener('click', async () => {
  try {
    const clipboardItems = await navigator.clipboard.read();
    for (const clipboardItem of clipboardItems) {
      const imageTypes = clipboardItem.types.find(type => type.startsWith('image/'))
      for (const imageType of imageTypes) {
        const blob = await clipboardItem.getType(imageType);
        // Do something with the image blob.
      }
    }
  } catch (err) {
    console.error(err.name, err.message);
  }
});

תמיכה בדפדפן

  • 86
  • 79
  • 13.1

מקור

הדרך הקלאסית

האזנה לאירוע של paste

הדרך הקלאסית להדבקת תמונות היא באמצעות Clipboard API (סינכרוני) שנותן גישה לשדה clipboardData שבתוך אירוע: paste. עם זאת, לשיטה הזו יש מגבלות, למשל בגלל שהיא סינכרונית, והדבקה של כמויות גדולות של נתונים עלולה לחסום את הדף.

document.addEventListener('paste', async (e) => {
  e.preventDefault();

  for (const clipboardItem of e.clipboardData.files) {
    if (clipboardItem.type.startsWith('image/')) {
      // Do something with the image file.
    }
  }
});

תמיכה בדפדפן

  • 66
  • 79
  • 63
  • 13.1

מקור

שיפור הדרגתי

לא ניתן לגשת באופן פרוגרמטי ללוח העריכה של המשתמשים (למשל, בלחיצה על לחצן) בדפדפנים שלא תומכים ב-Async Clipboard API. כך, כדי לגשת ללוח של משתמש באירוע paste, אפשר להשתמש ב-Async Clipboard API ולחזור אל Clipboard API (הסינכרוני).

באובייקטים מסוג ClipboardItem שמגיעים מ-navigator.clipboard.read יש שדה types שהוא מערך, וב-File אובייקטים שמגיעים מ-event.clipboardData.files יש שדה type שהוא מחרוזת. אפשר לבדוק באופן מותנה את כל אחד מהשדות type או types כדי לאתר תמונות בלוח:

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  const clipboardItems = typeof navigator?.clipboard?.read === 'function' ? await navigator.clipboard.read() : e.clipboardData.files;

  for (const clipboardItem of clipboardItems) {
    let blob;
    if (clipboardItem.type?.startsWith('image/')) {
      // For files from `e.clipboardData.files`.
      blob = clipboardItem
      // Do something with the blob.
    } else {
      // For files from `navigator.clipboard.read()`.
      const imageTypes = clipboardItem.types?.filter(type => type.startsWith('image/'))
      for (const imageType of imageTypes) {
        blob = await clipboardItem.getType(imageType);
        // Do something with the blob.
      }
    }
  }
});

קריאה נוספת

  • Clipboard API ב-MDN
  • ביטול חסימה של גישה ללוח העריכה ב-web.dev

הדגמה (דמו)

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link
      rel="icon"
      href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🎉</text></svg>"
    />
    <title>How to paste images</title>
  </head>
  <body>
    <h1>How to paste images</h1>
    <p>Hit <kbd>⌘</kbd> + <kbd>v</kbd> (for macOS) or <kbd>ctrl</kbd> + <kbd>v</kbd>
      (for other operating systems) to paste images anywhere in this page.
    </p>
  </body>
</html>

CSS


        :root {
  color-scheme: dark light;
}

html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin: 1rem;
  font-family: system-ui, sans-serif;
}

button {
  display: block;
}
        

JS


        document.addEventListener('paste', async (e) => {
  e.preventDefault();
  const clipboardItems = typeof navigator?.clipboard?.read === 'function' ? await navigator.clipboard.read() : e.clipboardData.files;

  for (const clipboardItem of clipboardItems) {
    let blob;
    if (clipboardItem.type?.startsWith('image/')) {
      // For files from `e.clipboardData.files`.
      blob = clipboardItem
      // Do something with the blob.
      appendImage(blob);
    } else {
      // For files from `navigator.clipboard.read()`.
      const imageTypes = clipboardItem.types?.filter(type => type.startsWith('image/'))
      for (const imageType of imageTypes) {
        blob = await clipboardItem.getType(imageType);
        // Do something with the blob.
        appendImage(blob);
      }
    }
  }
});

const appendImage = (blob) => {
  const img = document.createElement('img');
  img.src = URL.createObjectURL(blob);
  document.body.append(img);
};