2

I'm making a simple script to save some data on when a user access a page and leaves a page, along with some other page details. The script "works", but it doesn't work all the time. It seems to be working sporadically. I'm not getting console errors on the time it doesn't. The server side works fine.

window.onload = function() {


var timeStarted, i, c, l, timeLeft, pageData; 

timeStarted = new Date().getTime();

i = <?php echo $model->id; ?>;
c = <?php echo $model->cat; ?>;
l= <?php echo $model->loc; ?>;

window.onbeforeunload = function(){

timeLeft = new Date().getTime();

pageData = [{name: 'timeStarted', value: timeStarted}, 
            {name: 'i', value: job},
            {name: 'c', value: cat},
            {name: 'l', value: loc},
            {name: 'timeLeft', value: timeLeft}];

// Set url for JavaSCript
var url = '<?php echo Yii::app()->createUrl("item/viewedItemInformation"); ?>';

<?php echo CHtml::ajax(array(
        'url'=>'js:url',
        'data'=> "js: pageData",
        'type'=>'post',
        'dataType'=>'json',
        )); ?>;             
}
}

Note

I've just read this post window.onbeforeunload not working in chrome and I am using chrome. However I seem to get the same results in FF too.

What is the correct away about solving this?

Update I literally couldn't tell you what the situations it does and doesn't work. I am only using this function on item pages. Sometimes when i click on an item page and leave to visit another item it works other times it doesn't it really is that sporadic. What I can say is if I leave the item page for a non item page like the home page, no records are ever recorded.

Community
  • 1
  • 1
Jonnny
  • 4,939
  • 11
  • 63
  • 93
  • 1
    You cannot reliably make AJAX requests from `beforeunload`. That was never what that callback was meant for. – user229044 Sep 16 '13 at 03:45
  • @meagar Thanks, is there a work around to basically be able to pass the time a user loaded and then left a particular page at all? – Jonnny Sep 16 '13 at 03:46
  • 1
    Nope, there isn't, but depending on how accurate this has to be you can send a request on regular intervals, and when it stops, the user left, and HTML5 also has the [Page Visibilty API](http://www.w3.org/TR/page-visibility/) that could be useful. – adeneo Sep 16 '13 at 03:57

3 Answers3

5

onbeforeunload event is fired only if there was ANY interaction of the user with the site. Without ANY interaction event(e.g. click. and not including hover) on your page, onbeforeunload won't be fired.

aermin
  • 341
  • 3
  • 8
1

You may be able to use setTimeout to delay the tab from closing to do an AJAX request though if it's more than about 50 milliseconds most people will notice and will potentially avoid visiting your site.

The problem I think you're encountering is when the client's browser or operating system stop responding/working. To get 100% of the data in any situation would be extremely difficult if not outright impossible.

That being said you have not clarified what you think the situations are where the onbeforeunload event is not working so you've left your question too open to subjectivity. If you update your question and then comment on my answer I'll be happy to revise it in order to be more specific.


Things to try testing...

1.) Click a local link (a link on your site to another page on your site).

2.) Click an external link.

3.) Close the tab.

4.) Close the window.

5.) Kill the browser process via the task manager/equivalent.

6.) Pull the plug on the computer to emulate a power outage.


According to Mozilla Dev Network...

Since 25 May 2011, the HTML5 specification states that calls to 
window.showModalDialog(), window.alert(), window.confirm(), and window.prompt()
methods may be ignored during this event.

https://developer.mozilla.org/en-US/docs/Web/API/Window.onbeforeunload

So keep in mind that those methods may not work in some browsers when onbeforeunload is supported. In that case you can try to ping the server with an AJAX request and just watch your Apache access log for the request, usually you could send the JavaScript object/data/text/etc as an HTTP query such as request.php?q=javascript-object for example so you can visually confirm if the browser is doing what you think/want it to do.

John
  • 1
  • 13
  • 98
  • 177
  • Updated my code, it really is sporadic though. I also didn't realise it was so hard to achieve this reliably, which makes me wonder how Google analytics manages it. – Jonnny Sep 16 '13 at 22:57
  • I've added some scenarios for you to try out. I recommend trying each at least three or four times and write down what you find each time. This should help you conclude which scenarios you can reliably record data and which ones you can't. You can also use (preferably an older) version of Fiddler if you're using Windows to watch the requests of things such as Google Analytics. – John Sep 17 '13 at 09:13
  • Hi @John, I actually started looking at this again last night. I found it inconsistent using onbeforeunload() and I couldn't work out when it wasn't working using your helpful tips above, simply, sometimes it would work, other times it wouldn't. So I started looking at things like Piwiks. Then I just took a break from it all for a bit. In terms of reliability, I was brainstorming ideas and thought would just putting the information into a php session be easier. So every time you visit a page, it logs the ID and time. Then just calculating the time difference and writing a row to the DB? – Jonnny Nov 20 '13 at 15:24
  • @Jonnny Well I've updated my answer because I actually came across the MDN page and then found this page with my answer. Read the bottom section, I made another suggestion for visually confirming if `onbeforeunload` is working or not. – John Nov 21 '13 at 00:53
  • Thanks for that, from what I did previously and reading around on here for similar kinds of questions, I found the general consensus to be that the `onbeforeunload()` method was never built for the kind of purpose that I tried to use it with and it would always be very hit and miss whether data would have time to be recorded. Which I guess makes sense. Would you see the answer below as a reasonable alternative for what i'm attempting? – Jonnny Nov 21 '13 at 16:01
  • Move your script to `window.onload = function() {/*here*/}` and then see if it works; if not then fix it there first and *THEN* move it to the `onbeforeunload` event. You also haven't even clarified what browser you're having trouble with. So first make sure your script does what it needs to do (contacts your server from the client during the `onload` event) and then once you've confirmed (or fixed and *then* confirmed) test it with the `onbeforeunload` event; then let us know what happened and *ALSO* tell us what browser isn't working...and be sure to test in Chrome AND Firefox minimally. – John Nov 22 '13 at 06:30
0

So far this is how i've gone about it. Perhaps it will be helpful to others, if not and there are problems with this I shall delete the answer.

When I load the item page I update my DB with an identifier, category, id number, location, time started using NOW() and I update the time left as NOW() also.

Then within the page itself I run this javascript.

<script>

// Set url for JavaSCript
var url = '<?php echo Yii::app()->createUrl("item/viewedItemInformation"); ?>';
var item = '<?php echo $model->id; ?>';

var data = [{name: 'item', value: item},
    {name: 'async', value: false}];

// call function to see if still on page
var onPage = setInterval(function() {

<?php echo CHtml::ajax(array(
        'url'=>'js:url',
        'data'=> "js: data",
        'type'=>'post',
        'dataType'=>'json',
    )); 
?>              

}, 5000);

So that script runs every 5 seconds and calls another controller function that searches for the record in the DB and updates the time_ended with the time the controller function was called using NOW() also.

Hope that makes sense, I've only tested it a little bit, so I'll need to do a bit more with this. If anyone sees any problems with it let me know.

Update

So far this script seems to be working fine. I'm only working on my localhost and I have LinkedIn plugins and they seem to stop the script working. I keep getting this error: I've never noticed it before. I have recently reinstalled my XAMPP so might not be aware of any other differences between set up. I'm also not sure why the plugin would seem to stop my JS script working?

Origin http://localhost is not allowed by Access-Control-Allow-Origin.

Jonnny

Jonnny
  • 4,939
  • 11
  • 63
  • 93