JS onunload事件并不总是有效

5

我想要计算访客在某个页面上花费的时间,并将其存储在我的MySQL数据库中。

我考虑在window.onload上启动一个计时器,如下所示:

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);
}

当访问者离开页面时(window.onunload),我会通过XMLHttpRequest将时间发送到一个PHP文件,该文件会将其存储在我的数据库中:

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();
}

问题在于它并不总是有效。我会说,当我尝试它的时候,它只有3次中的1次有效。

可能是因为PHP和SQL没有足够的时间完全执行吗?

5个回答

4
由于onunload不可靠,我会考虑使用AJAX解决方案。我对此一窍不通,但我想到的是定时向计数器发送请求。我不会每秒钟都向计数器发送请求,因为这似乎太过频繁。
我会将时间分成你关心的持续时间,例如<5秒、<30秒、<1分钟、<5分钟、5+分钟。
然后,我会编写如下的JavaScript代码:
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();

}

这个技巧是,每发送一个count.php?q=30,你需要从count.php?q=5的计数中减去一个。这不必是程序化的,可以很容易地进行后处理。
为了澄清,如果你有3个访问者,一个花费3秒钟,一个花费20秒钟,另一个花费30分钟,你会看到以下计数:
  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

明白了吗?

非常感谢您的详尽回答。当然,它是有道理的,但不完全是我想要做的。我的目标是计算用户在页面上花费的所有时间之和(因此不需要统计数据)。我考虑每隔5秒钟使用AJAX将时间发送到数据库中,但这不会浪费服务器的计算能力吗? - LightDark
2
正如在其他地方所说的那样,如果这是目标,最好对轻量级服务器脚本进行30秒的ping。ping的频率取决于您拥有的网站类型,但在普通网站上,很多访问时间都不到30秒。也许您可以进行5秒钟的ping,然后将其减慢到30秒的ping。无论哪种方式,您都永远无法得到精确的数字。 - Jeff B

1

当onload处理程序运行时,页面正在被卸载。我不确定DOM或其他服务的状态有什么保证,但您肯定不能指望异步事件或超时触发。虽然不是标准的一部分,但今天大多数(全部?)浏览器都提供了一个onbeforeunload钩子,其工作方式与其他事件处理程序类似,但如果选择,您还可以返回false以取消卸载。


谢谢你的回答,我尝试了onbeforeunload,但在Chrome上完全不起作用 :/ - LightDark
有趣。应该可以工作吧?https://dev59.com/D0fRa4cB1Zd3GeqP8mWY - peller

1
您可以按照建议设置一个定时器,以任意频率运行(每隔几秒钟),并将信息存储在 cookie 中。如果需要,在想要执行更多操作而不仅仅是向用户显示值时,可以将此信息发送到服务器。因此,不要在每个周期都将其发送到服务器,只有在想要使用该信息进行某些操作时才将其发送到服务器(例如向用户显示他在您网站上各个页面上花费的时间列表)。

谢谢你的回答。我想做的就是简单地获取所有访问者在某个特定页面上花费的时间总和,并将这些信息存储在数据库中(不必对用户可见)。同时,这些信息应该尽可能准确,并且不需要太多的数据库请求(我不想每秒都发送和保存时间)。 - LightDark
1
我所知道的没有可靠的方法可以做到这一点,除非定期向服务器发出ajax调用,自然而然,您需要保持合理的频率。如果该调用的端点非常轻量级,则不应该太大问题。实际上,任何方法都不会非常准确,因此我认为30秒左右的分辨率就是您能够期望的最多了。即使卸载事件正常工作,数据也会受到诸如人们离开计算机但网页仍然打开、切换到其他站点的选项卡等干扰。 - Allan Nienhuis

0

onunload并不是追踪任何事情的可靠方式,例如请参见http://www.google.com/support/forum/p/Google+Analytics/thread?tid=6496cd5d4b627c0e&hl=en

解决这个问题的最佳方法是跟踪用户上次访问页面的时间。例如,从客户端每分钟调用一次跟踪器。像Google Analytics这样的分析系统实际上只是假设用户在最后一个服务的页面之后离开了。也就是说,如果我阅读了页面然后单击了某个内容,它会刷新计数器。但是如果没有后续的点击发生,那就意味着我已经离开了。


好的,我明白了...即使你通过输入不同的URL离开页面,它也无法工作 :( 那么,我还有什么其他方法可以解决我的问题(计算和存储在某个页面上花费的时间)? - LightDark

0

另一个选项是将数据保存在localStorage/cookie中,并在下次用户访问网站时发送数据;假设用户留在网站上或者会再次返回。

使用Date()计算时间比使用setInterval/setTimeout更好。
使用addEventListener而不是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);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接