תאריך פרסום: 3 ביולי 2026
אם השתמשתם בעבר בכלי לעריכת מסמכים, באפליקציה של גיליונות אלקטרוניים או בכל אפליקציה אחרת שבה הדבקת תוכן מלוח העריכה היא אינטראקציה ראשונית, אתם יודעים מה הציפייה של המשתמשים: ההדבקה צריכה להיות מיידית.
לצערנו, במקרים רבים אפליקציות אינטרנט קוראות יותר נתונים מלוח העריכה ממה שהן צריכות, מה שגורם להדבקת תוכן להרגיש איטית יותר ממה שהיא צריכה להיות, מבזבז מחזורי CPU ומשתמש בזיכרון לנתונים שהאפליקציה אף פעם לא צריכה.
קריאה סלקטיבית של פורמטים פותרת את בעיית הביצועים הזו על ידי שינוי Async Clipboard API כדי לעמוד טוב יותר בציפיות המשתמשים ולספק למפתחים את מה שהם צריכים – לקרוא רק את הפורמטים שהאפליקציה באמת צריכה.
הבעיה: קריאה של כל פורמטי הנתונים מראש
כשמשתמש מעתיק מכל אפליקציה – למשל, Excel, Google Docs או Photoshop – האפליקציה בדרך כלל כותבת כמה ייצוגים של אותם נתונים ללוח המערכת: טקסט פשוט, HTML, תמונה ולפעמים פורמטים מותאמים אישית. אפליקציית האינטרנט שקוראת את הנתונים (כשהמשתמש מדביק מלוח העריכה) לא צריכה בדרך כלל את כל הייצוגים האלה. יכול להיות שבעורך גיליונות אלקטרוניים תרצו להשתמש ב-HTML. יכול להיות שיהיה רצוי בכלי לעריכת טקסט להציג פקודה של "הדבקה כטקסט רגיל". יכול להיות שעורך קוד ירצה רק את הטקסט ולא שום דבר אחר.
עד עכשיו, האפליקציה navigator.clipboard.read() אחזרה כל ייצוג זמין של הנתונים מלוח הגזירה של המערכת, ביצעה ניקוי (שיכול לקחת זמן, במיוחד עם מטען ייעודי של HTML או תמונות גדולות), ושמרה את הבייטים בזיכרון, גם עבור הפורמטים שהאפליקציה לא תשתמש בהם לעולם. ככל שהמטען הייעודי הלא מנוצל גדול יותר, כך המשתמש נאלץ לחכות יותר זמן, וכך הכרטיסייה צורכת יותר זיכרון, בדיוק כשחשוב שההדבקה תהיה מהירה.
הפתרון: ספירה בלבד, ואז קריאה לפי דרישה
החל מגרסה 149 של Chrome ו-Edge,
אובייקטים ClipboardItem שמוחזרים על ידי השיטה navigator.clipboard.read() מתארים רק מה יש בלוח, במקום לתת גישה לנתונים באופן מיידי. הבייטים בפועל של פורמט נתון נקראים מלוח הגזירה רק כשמבצעים קריאה ל-ClipboardItem.getType(mimeType).
ממשק ה-API לא השתנה. אותו קוד שכתבתם אתמול עדיין פועל היום, ומספק לכם יתרונות מיידיים בביצועים:
// Enumerate the available MIME types.
// No payload is fetched yet.
const items = await navigator.clipboard.read();
for (const item of items) {
if (item.types.includes('text/html')) {
// Only read the HTML data.
// The data is fetched (and sanitized) here only.
const blob = await item.getType('text/html');
useThePastedHtml(await blob.text());
break;
}
// text/plain, image/png, or any other custom formats
// stored in the clipboard are never read.
}
יתרון נוסף בביצועים הוא שהשמירה במטמון מתבצעת באופן אוטומטי. הפעלת getType('text/html') פעמיים באותו ClipboardItem קוראת מהלוח של המערכת רק פעם אחת ומחזירה את Blob שנשמר במטמון בהפעלה השנייה.
היתרונות
זמן אחזור. הפעולות שהיו צריכות להתבצע כשבוצעה קריאה ל-navigator.clipboard.read()
כמו העתקת בייטים בין תהליכים, ניקוי HTML או פענוח תמונות, מתבצעות עכשיו רק עבור הפורמטים שהאפליקציה שלכם צריכה בפועל. אפליקציות שקוראות רק פורמט אחד מקבלות את רוב החיסכון. ככל שהפורמטים שדילגתם עליהם גדולים יותר, כך הרווח גדול יותר. היתרון הכי גדול הוא ב-HTML, כי ניקוי של נתוני HTML הוא יקר.
זיכרון. בעבר, קריאה ל-navigator.clipboard.read() כשהלוח
הכיל תמונה בגודל 50MB, גרמה לכך ש-50MB היו צריכים לשמש בזיכרון של רכיב הרינדור של דף האינטרנט עד ש-ClipboardItem שוחרר, גם אם האפליקציה הייתה צריכה רק את הייצוג של הנתונים ב-text/plain. עכשיו, רק הפורמטים שהאפליקציה קוראת כשמפעילים את ClipboardItem.getType() משתמשים בזיכרון בתהליך העיבוד. באפליקציות שבהן נהוג להדביק, כמו עורכי מסמכים, כלי כתיבת אימיילים או כלי עיצוב, השינוי הזה מצמצם באופן משמעותי את השימוש בזיכרון במהלך ההדבקה, במיוחד בסשנים עם כמה כרטיסיות שבהם כמה עורכים קוראים את הלוח במקביל.
לא נדרשת הסכמה. כל אתר באינטרנט נהנה מהיתרונות האלה באופן אוטומטי. אתם לא צריכים להעביר קוד או להגביל קוד מאחורי לוגיקה של זיהוי תכונות.
שינוי אחד בהתנהגות שכדאי להכיר
קריאת הנתונים בשלב מאוחר יותר גורמת לשינוי קל בהתנהגות: אובייקט ClipboardItem כבר לא מהווה תמונת מצב קפואה של הלוח ברגע שהקריאה ל-navigator.clipboard.read() נפתרת.
כשקוראים ל-read() ומוחזר ClipboardItem, הפריט הזה הוא רק lazy handle לנתונים בלוח הגזירה של המערכת.
אם התוכן בלוח משתנה בין הקריאה ל-read() לבין קריאה מאוחרת יותר ל-getType(), הקריאה הזו תידחה עם InvalidStateError. הדבר נכון גם לגבי פורמט שקראתם בעבר בהצלחה באותו ClipboardItem.
// Read from the clipboard.
const items = await navigator.clipboard.read();
// Get the first ClipboardItem.
const item = items[0];
// Actually fetch the data now, which works fine.
const text = await item.getType('text/plain');
// The user writes something new to the clipboard
// by copying from another app, or your own code writes to
// the clipboard again.
await navigator.clipboard.writeText('something new');
// Fetching the data from the earlier ClipboardItem
// rejects with InvalidStateError, because the contents
// of the clipboard changed.
const html = await item.getType('text/html');
זה קורה כי החזרת נתונים שכבר לא משקפים את מה שבלוח הייתה מבלבלת ומובילה לבעיות שקשה לנפות.
בפועל, השינוי לא נראה ברוב הקוד, כי בדרך כלל האפליקציות קוראות ל-read() ול-getType() ברצף בתוך אותו handler של הדבקה. עם זאת, כדאי לשים לב למקרים שבהם יש קוד ששומר על ClipboardItem בגבולות אסינכרוניים.
הנה דוגמה למקרה שבו השינוי הזה עלול לגרום לבעיה: שמירת אובייקטים במטמון של ClipboardItem ב-window, הצגתם בממשק משתמש של היסטוריית הלוח או המתנה לאינטראקציה של המשתמש לפני קריאה ל-getType(). כדי למנוע בעיות בתרחיש כזה:
- עוטפים קריאות ל-
getType()בבלוקים שלtry / catch, ומתייחסים ל-InvalidStateErrorכאות לכך שהלוח עבר הלאה והקוד צריך לקרוא שוב. - אם אתם צריכים לשמור את הנתונים בלוח, אתם צריכים להעתיק את
Blobלמבנה משלכם ברגע שאתםgetType()אותו. אל תתייחסו אלClipboardItemכאחסון לטווח ארוך. - כדאי להשתמש באירוע
clipboardChangeכדי לבטל מראש את התוקף של נקודות אחיזה לא עדכניות.
תקנים ותמיכה בדפדפנים
קריאה סלקטיבית של פורמט היא שינוי במפרט של ממשקי ה-API של הלוח, שנובע מהצוות של פלטפורמת האינטרנט של Microsoft Edge ומהצוות של קבוצת העבודה בנושא עריכה באינטרנט.
גם דפדפן Safari תומך בהתנהגות הזו, וב-Firefox ציינו עמדה חיובית לגבי הסטנדרטים וכוונות להתאים את הדפדפן.
שיפור שקט אבל משמעותי בביצועים ובפעולה ההדדית של Clipboard API, עם Chromium שעכשיו מתכנס לאותו מודל.