5

I want to count how much time visitors spend on a certain page and store it in my MySQL DB.

I thought of just starting a timer on window.onload like this:

window.onload= startCount;
window.onunload= sendCount;

var b=0;
var y;
function startCount() {
    document.getElementById('livecount').innerHTML=b;
    b=b+1;
    y=setTimeout("startCount()",1000);
}

and after the visitor leaves the page (window.onunload), I'm sending the time through an XMLHttpRequest to a PHP file, which will store it in my DB:

function sendCount() {
    clearTimeout(y);    
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }  
xmlhttp.open("GET","count.php?q="+b,true);
xmlhttp.send();
}

The problem is it doesn't always work. I would say it works like 3 out of 10 times I try it. Could it be that there is not enough time left for the PHP and SQL to be completely executed?

Crescent Fresh
  • 115,249
  • 25
  • 154
  • 140
LightDark
  • 73
  • 1
  • 6

5 Answers5

4

As onunload is unreliable, I would consider an AJAX solution. I am not an expert in this at all, but the thought that comes to mind is a ping to your counter every so often. I wouldn't ping your counter every second, as that seems excessive.

I would break down times into durations you care about, such as <5 seconds, <30 seconds, <1 minute, <5 minutes, 5+ minutes.

Then I would write a piece of JavaScript like so:

window.onload = soCounter;

var iter=0;
var times=[0,5,30,60,300];  // Your duration choices

function doCounter() {

    sendCountPing(times(iter));

    iter++;

    if(iter < times.length) {
        setTimeout(doCounter, times[iter]*1000);
    }
} 

function sendCountPing(dur) {
    if (window.XMLHttpRequest)
    {  // code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp=new XMLHttpRequest();
    }
    else
    {  // code for IE6, IE5
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }  

    xmlhttp.open("GET","count.php?q="+b,true);
    xmlhttp.send();

}

The trick is that for every count.php?q=30 that gets sent, you need to subtract one from the count.php?q=5 count. This does not have to be programmatic, it is easily post-processed.

To clarify, if you had 3 visitors, one spending 3 seconds, one spending 20 seconds, and one spending 30 minutes, you would see the following counts:

  0:  3   // 3 people spent at least 0 seconds
  5:  2   // 2 people spent at least 5 seconds
 30:  1
 60:  1
300:  1   // 1 person spent at least 5 minutes

Make sense?

Jeff B
  • 29,943
  • 7
  • 61
  • 90
  • Thank you very much for your extensive answer. Yes, it makes sense of course, but it's not quite what I want to do. My goal is to make a sum of all the time users have spend on the page (so no statistics are required). I thought of just sending the time to the DB with AJAX every 5 seconds or so, but isn't it a huge waste of the servers computing power? – LightDark Nov 29 '10 at 22:44
  • 2
    As is said elsewhere, if this is the goal, you are best off doing 30 second pings to a lightweight server script. The frequency of the pings depends on the type of site you have, but on normal sites, a lot of visits are less than 30 seconds. Perhaps you could do a 5 second ping, and then slow it to a 30 second ping. Either way, you will never get an exact number. – Jeff B Nov 30 '10 at 19:39
1

by the time the onload handler runs, the page is in the process of being taken down. I'm not sure what guarantees you have for the state of the DOM or other services, but you certainly can't expect asynch events or timeouts to fire. Though not part of the standard, most (all?) browsers today provide an onbeforeunload hook which works much the same way as other event handlers, except that you can also return false to cancel the unload if you choose.

peller
  • 4,435
  • 19
  • 21
  • Thanks for the answer, I tried onbeforeunload and it didn't work at all with Chrome :/ – LightDark Nov 30 '10 at 11:07
  • Interesting. Should work? http://stackoverflow.com/questions/803887/can-i-pop-up-a-confirmation-dialog-when-the-user-is-closing-the-window-in-safari – peller Nov 30 '10 at 21:28
1

You could set up a timer as suggested which runs as often as you want (ever few seconds or so), and store the information in a cookie (or cookies). You can send this information to the server if needed when you want to do something more than displaying the value to the user. So don't send it to the server every cycle, just send it to the server when you want to do something with that information (like display to the user a list of all the time he spent on various pages on your site).

Allan Nienhuis
  • 3,961
  • 2
  • 23
  • 19
  • Thank you for your answer. What I'm trying to do is simply get the sum of the time spend by all visitors on one certain page and store this information in the database (doesn't have to be visible to the user). At the same time this information should be as accurate as possible and should not require too many DB requests (I don't want to send and save the time on like every single second). – LightDark Nov 30 '10 at 11:05
  • 1
    There is no reliable way of doing this that I know of without regular ajax calls to the server, which naturally you would need to keep to a reasonable frequency. If the endpoint of that call is very lightweight it shouldn't be too big of a deal. Realistically, ANY method won't be very accurate, so I think a resolution of 30 seconds or so is as much as you could expect anyways. Even if the unload event worked properly, the data would be polluted with occurrences like people walking away from their computer with the webpage still up, switching tabs to other sites, etc, etc. – Allan Nienhuis Nov 30 '10 at 16:15
0

Another option is to save the data in localStorage/cookie and send it the next time that the user comes to the website; assuming the user stays or ever returns to your website.

It's better to use Date() calc instead of setInterval/setTimeout to count the time.
It's better to use addEventListener instead of onload = func.

sendPreviousStats();
window.addEventListener('load',loadFunc);
window.addEventListener('unload',leaveFunc);

function loadFunc() {
    window.startTime = new Date();
}

function leaveFunc() {
    var msec = new Date().getTime()-window.startTime.getTime();
    var seconds = Math.round(msec/1000);
    localStorage.setItem('leaveSeconds',seconds);
}

function sendPreviousStats() {
    var duration = localStorage.getItem('leaveSeconds');
    if (duration !== null && duration !== undefined) {
        localStorage.removeItem('leaveSeconds');
        sendCountPing(duration);
    }
}
oriadam
  • 7,747
  • 2
  • 50
  • 48
0

onunload is not a reliable way to track ... well, pretty much anything. for example see http://www.google.com/support/forum/p/Google+Analytics/thread?tid=6496cd5d4b627c0e&hl=en

As far as solving the issue, the best way is to track last time the user was on the page. For example call the tracker every minute from the client. Analytics systems like Google Analytics actually just assume user left after the last page that was served. I.e. if i read the page and then click on something, it refreshes the counter. But if no subsequent hits happen, that means that I left.

Artemiy
  • 1,969
  • 14
  • 19
  • OK, I see...it doesn't even work when you leave the page by typing in a different URL :( Well, how else could I solve my problem (count and store the time spend on a certain page)? – LightDark Nov 29 '10 at 19:51