상세 컨텐츠

본문 제목

Page Change Event Code That Works in ALL Devices and Browsers - JavaScript

본문

Detecting page change event in all devices is a difficult job because of different ecosystem.  Especially iOS has its own browser ecosystem that are different and sometimes incompatible with other systems, even with macOS within the same company.  The iOS browser ecosystem is applied to ALL different browsers in iOS, so if you use Chrome in iOS, it is NOT the same Chrome you use on other systems.  Therefore, do not think that Chrome will behave the same on all devices just because it is the same browser.
 
I had used onbeforeunload event before and it seemed to work generally fine on all devices until I found that iOS just ignores this event.  Apple deprecated this event from iOS so this would not work at all.  They recommend using pagehide event, but this event is not reliable either.  
 
After countless researching and testing, I figured that document.onvisibilitychange event worked most reliably.  This event gets triggered even when a user changes to a different app while viewing a page in a browser. 
 
The reason I needed this code was that I had to send some page analytics when a user changes a page (e.g. last position of a streamed video so that it can conitnue from there when the user opens goes back to the page).  I will explain how I achieved this as an example, so you can successfully complete your own similar tasks.
 

반응형

 
The purpose of this article is how to send data when a user moves from one page to another page or a different site, NOT showing any kinds of alert windows before changing the page.   In order to achieve this, I need to explain in two separate steps as below:
 

1.  The most compatible and reliable page change event
2.  A compatible method to send data during this event


1.  Event

As I mentioned in the introduction, the most reliable and compatible JavaScript event for page change is document.onvisibilitychange.  Below is a sample code on how to set the event listener (in React, but the concept is the same on JavaScript). 
 
Basically you are creating an event listener for document.onvisibilitychange and check for document.visibilityState === "hidden", which means that the page is either swapped with a different page or moved behind another application.

// PAGE CHANGE EVENT
useEffect(() => {
    const handlePageLeave = event => {
        if (document.visibilityState === "hidden") {
            updatePageStats();
        }
    };

    document.addEventListener(
        "visibilitychange", handlePageLeave);

    return () => {
        document.removeEventListener(
            "visibilitychange", handlePageLeave);

    };
});

You may have already knonw this because there are many articles describing this alternate method.  However, sending data during this event is a tricky task, and not many articles clearly descibe how.
 

728x90

2.  Method

Once you have the event listener in place, you must define the event to trigger when page change event happens (updatePageStats function in my example).  During the page change event, the only way to send data to an API server is to use navigator.sendBeacon function.  However, this function has many limitations.  It only supports custom headers in Blob format and sending Blob using this method does not always work 100% successfully.  
 
In my case, I had to send data to an API server on a different domain (cors), and the data format was JSON.  Sending JSON data format via cors is a difficult job without help of custom headers.  The combination of both was not possible (at least for me), so I searched an alternate solution. 
 
I found that fetch command with keepalive: true option would behave the same way as navigator.sendBeacon, so the browsers would allow sending data during the event.  fetch command is a lot more flexible because it has various custom options.  
 
Below is a simplified version of my code (again, React code but it is JavaScript):

const updatePageStats = useCallback(() => {
    // JSON DATA TO SEND
    let analyticsData = JSON.stringify({
        userId: userId,
        movieId: movieId,
        recentEndPointSeconds: endPointSeconds
    });
    
    fetch("ADDRESS OF API SERVER", {
        keepalive: true,
        method: 'POST',
        mode: 'cors',
        headers: {
            "Content-Type": "application/json",
        },
        body: analyticsData
    });
}, [userId, movieId, endPointSeconds]);

Of couse you must use your own fetch options because every API server has different settings.  However, this code worked 100% on all devices across the board (all browsers on Windows, Android, macOS and iOS).


Hope this helped!  Thank you

 

 

728x90
반응형

관련글 더보기