• בלוג
  • היום למדתי: הפקודה sendBeacon ב JavaScript

היום למדתי: הפקודה sendBeacon ב JavaScript

18/06/2025

בעיה נפוצה בשליחת אירועים מהדפדפן לשרת היא הציפיות שלנו. אנחנו רוצים לדווח לשרת כשמשתמש עוזב עמוד מסוים כדי לדעת כמה זמן משתמש בילה באותו עמוד, אבל המשתמש מעדיף באותו זמן לצאת מהדף ולא לחכות שנגמור לדווח לשרת שלנו ובטח לא לחכות לשרת שיחזיר תשובה.

דפדפנים נוטים לקחת את הצד של המשתמש בסיפור הזה וכשמנסים לשלוח fetch או XMLHttpRequest ביציאה מהעמוד הדפדפן פשוט לא ישלח את ההודעה. לפי התיעוד ב MDN אנשים השתמשו בכל מיני טריקים כדי לגרום לדפדפן להישאר עוד קצת בעמוד האהוב עליי היה ליצור אלמנט img שה src שלו הוא כתובת השרת לדיווח על יציאה.

פקודת navigator.sendBeacon היא הדרך של יצרני הדפדפנים לעזור לשני הצדדים: גם לאפשר למפתחים לקבל אירועים כשמשתמשים עוזבים את העמוד וגם לאפשר למשתמשים לעזוב עמודים מהר בלי לחכות לדיווחי הסטטיסטיקות. כך זה עובד:

  1. אנחנו מפעילים פקודת navigator.sendBeacon שחוזרת מיד בלי לחכות לבקשת התקשורת.

  2. משתמש יכול להמשיך לעזוב את העמוד.

  3. כשלדפדפן יהיה זמן הוא ישלח את הדיווח לאתר שלכם.

אם השרת שלכם מצפה לקבל מידע בפורמט JSON תוכלו להשתמש בקוד הבא כדי לשלוח את המידע מהדפדפן לשרת בלי לחכות לתשובה:

const url = 'https://example.com/collect';
const data = {
  event: 'pageUnload',
  timestamp: Date.now(),
  userId: 'abc123'
};

// Convert the JSON object to a string
const jsonString = JSON.stringify(data);

// Convert the string to a Blob with the appropriate MIME type
const blob = new Blob([jsonString], { type: 'application/json' });

// Send the beacon
navigator.sendBeacon(url, blob);

הקוד שולח את המידע בתור blob בשביל לקבוע את ה content-type של הבקשה (מה שגורם לפריימוורק צד שרת בדרך כלל לפענח את ה JSON בצורה אוטומטית). אם זה לא חשוב לכם ואתם מוכנים לשלוח טקסט ולפענח את ה JSON בשרת בעצמכם תוכלו לכתוב גם:

navigator.sendBeacon(url, JSON.stringify(data));

והרבה פעמים תרצו להפעיל את זה כשמשתמש עוזב את העמוד ובשביל זה כדאי להתחבר לאירוע visibilitychange באופן הבא:

document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    navigator.sendBeacon("/log", analyticsData);
  }
});