If the website wants to send analytics after the user has finished the page, but the page may have been unloaded by then, sending an asynchronous request may not be sent by the browser at this point. So what should we do?

First, we might think of the beforeunload event. It enables the web page to trigger a confirmation dialog to ask the user if they want to leave the page. When the user enters unsaved data but accidentally closes the page or refreshes the page, the data will be lost. The use of beforeunload allows the user to confirm twice. Like the screenshot below:

![Browser beforeunload event confirmation dialog example asking user to leave or cancel](https://img.upweb.dev/content/21f95d72-579x180.png)

However, clicking Leave or Cancel does not have a corresponding callback function for us to use, so it can only be used as a secondary confirmation.

Next is the unload event. This event is fired when the document is unloaded. It’s after the beforeunload event. If we send an asynchronous request in this event, as I said at the beginning, the browser may choose not to send this asynchronous request, or it may abort this request.

So can we send blocking synchronous XMLHttpRequest requests like below?

![Synchronous XMLHttpRequest example in unload event for reliable request sending](https://img.upweb.dev/content/581f3074-1159x768.png)

The answer is yes. But this prevents the document from being unloaded, which slows down the browser to navigate to the next page. The next page can’t avoid this, because the new page looks slow, even if it’s the previous page’s fault.

Similar to this: create an `<img>` element and set its src, create a synchronous loop that does nothing for a few seconds, etc.

So these are not good solutions. Fortunately, browsers provide a targeted API for this — `Navigator.sendBeacon()`

**This event allows profiling data to be sent asynchronously at the end of a page session without delay blocking the loading of the next page. And it guarantees that data is sent reliably.**

Its syntax is this: `navigator.sendBeacon(url [, data])`. data is an optional parameter, it can be an `ArrayBuffer`, a `TypedArray`, a `DataView`, a `Blob`, a string literal or object, a `FormData` or a `URLSearchParams` object.

Here is a simple example:

![Navigator.sendBeacon API example using visibilitychange event for reliable data transmission](https://img.upweb.dev/content/2f8ee4f7-1292x489.png)

You can see that I use the `visibilitychange` event here instead of the `unload` or `beforeunload` event. This is because both events are extremely unreliable. It is especially serious under mobile devices, such as when the user closes the browser application using the application manager of the mobile phone, when switching between different applications, etc.

Also, these two events are not compatible with modern browsers’ [back/forward cache](https://web.dev/bfcache/) (bfcache). This is bad for performance. So the best event to signal the end of a user session is the `visibilitychange` event. In browsers that don't support it, the next best option is `pagehide` event, which is also not fired reliably, but which is bfcache-compatible.

Finally, let’s look at the type of data. When we use `URLSearchParams` like above, the request header `content-type` will be set to `text/plain;charset=UTF-8`. Similarly, if we use `FormData`, then the `content-type` of the request header will be set to `multipart/form-data`. But if we use `Blob`, we can customize request headers and request content like this:

![Navigator.sendBeacon with Blob example showing custom request headers and content type](https://img.upweb.dev/content/cbbb9d88-1000x360.png)

Do you know other ways? Feel free to share.

_If you found this helpful, consider [subscribing to my newsletter](https://upweb.dev) for weekly web development insights and updates. Thanks for reading!_
