1

I have a web app where a parent page displaying a list of records opens up a new tab ('child') to edit a clicked-on record. I want to track who has a page open, so I can display a message if more than one person is editing a unique record. This means reporting when a page is closed. I have assigned each page a GUID to facilitate recognition of the page instance.
So javascript in the browser needs to detect several scenarios:

  1. browser tab closed
  2. browser refresh
  3. browser navigation to hyperlink
  4. browser navigation forward/back

At the moment, all of these appear to trigger the window.onbeforeunload event. However I use this event to warn of changes in the underlying data, which means the event returns the confirmation text, and there is no way of knowing in this event if the user subsequently confirms or cancels the page unload. So I can't use this event to track page closure.

According to a number of sources the window.onunload event should be triggered in all of the above scenarios (and if it was, I could use it), but testing under Chrome on Windows is only triggering this event in scenario 1 (when the tab is closed). It works fine for that.

I'm pretty surprised by the lack of information around this - surely it's a bread and butter requirement in modern sites?

Has window.onunload been deprecated lately in some scenarios, or in some scenarios in some browsers? Without a reliable hook that takes place when the page is about to be replaced with some other information, it's impossible to monitor closing of a page. Any other workarounds?

I know that the two unload events suppress blocking functions (such as alerts) in the handler. However they appear to do hit breakpoints, do a console.log and allow Ajax calls just fine. I'm pretty sure they are not being fired in events 2,3 and 4 - it's not just that my debugging is being blocked.

4
  • 1
    In general you really cannot reliably tell when a page has been left by the user, and there's not much the browser can even do about it. Generally sites keep a session timer and close a session down after a certain period of time. Commented Jun 27, 2021 at 14:01
  • Why not open a websocket or eventsource connection from the page? The server will know if a connection drops, and then you can be reasonably sure the page was unloaded somehow. And that connection would give you an avenue to push notifications about simultaneous editing of the same record. Commented Jun 27, 2021 at 14:05
  • @David784 agreed, HTTP does not seem like the mechanism of choice in trying to keep track of concurrent activity around some resource. Commented Jun 27, 2021 at 14:11
  • Yeah, I was kind of expecting this to be the case, but it sucks. I could use some kind of keep-alive signal, like your websocket, but it's incredibly resource intensive and complex by comparison. Also, what if the client closes the lid on their laptop and the page goes to sleep for a couple of hours, and then they open it and continue on where they left off. All these scenarios would be easily handled by onunload. Commented Jun 27, 2021 at 22:06

1 Answer 1

3

While there appear to be answers on SO already (most of which don't work or are deprecated), I posted this because browser events are a shifting-sands scenario as security issues evolve, so I wanted to find out where we are in 2021.

Actually, it looks as if this might be the solution: https://developers.google.com/web/updates/2018/07/page-lifecycle-api#the-unload-event

Google discourages use of the unload event because it is (a) unreliable on mobiles and (b) blocks the caching of pages. It is also advised to only add the beforeunload event just before it is used, and to remove it afterwards, because it also blocks the caching of pages (I note that this is not really practical for me, however, as I use it to guard against unintentional closing of a page after a possibly significant amount of data has been entered, and this could happen at any moment).

So as of July 2018, and still best practice as of July 2021, this would be the recommended way to detect the unloading of a page:

    const terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload';

    addEventListener(terminationEvent, function(event) {
        // handler code here ...
    }, { capture: true });

This has been tested in a small ASP NET Core project using an AJAX callback to report the page termination, and appears to work reliably in Chrome and Edge. Also works in IE11 as long as

<meta http-equiv="x-ua-compatible" content="IE=edge">

is present.

NOTE: as per the comment below, I have been completely unable to get this to work reliably. If anyone has solved this problem, I'd be more than happy to pay a bounty to find a solution.

Sign up to request clarification or add additional context in comments.

5 Comments

Just a note that this did appear to work reliably in my test setup, but when I implemented it in the actual application, there were problems. I haven't had time to go back and work out what the issues were. A huge amount of time to spend on something so fundamental!
It is not an answer to your question, but for the problem you try to solve. Don't work with 'inactivity' but with activity instead. stackoverflow.com/a/3610461/3090890 and about the reliability of ajax requests to terminate sessions: stackoverflow.com/questions/3584288/…
Thans @Piemol, but one of the scenarios I need to detect is: user enters some data on a page, goes to lunch and closes the lid of the laptop (so browser and computer go to sleep for an hour), then comes back, reopens the computer and keeps entering data, then hitting save. I don't think there is any other way of dealing with this other than a reliable unload
You already track 'checkouts', add 'form activity' ( fire ajax requests each x second) to that checkout, purely to inform other users. Add a 'version' column to the records (optimistic locking) which increment on each succesful save. If you can't build a 'snapshot' of a specific version of the record: add a json encoded record as hidden input. When a user 'saves' after long inactivity: server side is checked if the 'version' attribute matches, if not, present the user for each field: snapshot; current; posted value to solve the diff. On successful save, increment version and
Again, this is switching from pessimistic concurrency (which I want) to optimistic concurrency. No user is ever going to accept the 'diff' presentation, even it it wasn't a fiendishly difficult programming problem. This means that we say 'another user has edited this record, do you want to replace the data' and either they lose their updates or you lose yours. This is not acceptable, hence I want to use pessimistic concurrency that involves locking out other users from that record until the edit is complete. Unfortunately, it appears that is not possible to do reliably.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.