I’m trying to build a user replay session functionality for a website and I’m using rrweb library to do that.
What this library does is when recording: it captures all the events in the webpage and I’m able to save those events by storing them in an array
and when I want to replay the session I’ll just pass the array
to the replay function and that function handles the session replay.
Currently for testing purposes I’m saving this array in my sessionStorage
and every time a new event is emitted I’ll just get the array
then push that new event into it then save the array
again in my sessionStorage
:
rrweb.record({
emit(event) {
const sessions = JSON.parse(sessionStorage.getItem('sessions')) || [];
sessions.push(event);
sessionStorage.setItem('sessions', JSON.stringify(sessions));
},
});
However, for production, instead of saving that array in my sessionStorage
and then updating it every time a new event is emitted, I would like to save that array
in my database and I want to call the function that would save the array
to my database once either when the user logs out
or when the user decides to close the website (like by pressing the X
button).
The first part -when the user logs out
– is pretty straight forward, I’ll just add an eventListener
on the logout
button, it’s the second part -when the user decides to close the website- that is giving me some headache.
I know that there is the beforeUnload
event however after a quick search it became clear to me that it’s unreliable so basically what I’m looking for is a reliable way to determine when the user closed my website so I could fire an async function
that would save the array
to my database.
2
Answers
For this kind of problem you should make a database schema to store this and fire a function that takes the event and save that events in the database as usual.
And for another problem of the after closing website you may call a function like:
May this will help you for you.
Here are my thoughts on what you can do to mitigate the unreliableness of the
beforeunload
event:sessions
array. Assuming we are talking about a single page that you wish to replay, this count could be saved as a JavaScript variableeventCount
. Better yet would be to get the count from the server-side table in case for some reason not all the events recorded were successfully saved the last time the page was closed.checkEvents
to be called everyN
seconds (you must decide how often you want to call this function) using the WindowsetInterval
method. This function will look at the current event count (variablenewCount
) and if greater than the currenteventCount
value, then the NavigatorsendBeacon
method can be used to POST a request to the server passing all the events that have been added since the last call (i.e.JSON.stringify(sessions.slice(eventCount, newCount))
and when the request completes assigneventCount = newCount
ifnewCount
is >eventCount
. Note that while the asynchronoussendBeacon
request is running it is possible that new events have been generated and that’s why we update the event count withnewCount
rather than the current size of thesessions
array.sendBeacon
request when the page is unloaded. Since we know that thebeforeunload
andunload
events are not reliable, then we use thevisibiltychanged
event (if supported by the browser) and when the new visibility state is ‘hidden’ we update the server. The documentation discusses those user actions that cause this event to be fired (not just on closing the page). If, however, the browser does not support this event, then thepagehide
event will have to be used.It’s not discussed within the documentation for the
Navigator.sendBeacon
method whether there can be multiple concurrent requests. Assuming there can be (it’s an asynchronous call), there is slight possibility the user might decide to navigate away from the page or close it while asendBeacon
request is currently in progress because of thesetInterval
call. Then the server URL to which you post this request should probably lock the table while doing the insertions so that any subsequent POST to this URL will block until the previous one completes. Another solution I would propose if your table uses some sort of sequence number as the primary key would be to also pass to the server the starting array index of the first event being passed and the server would use that to explicitly set the sequence number for each event inserted. Then concurrent updates could run without having to lock the entire table.