简体   繁体   English

JS onunload事件并不总是有效

[英]JS onunload event won't always work

I want to count how much time visitors spend on a certain page and store it in my MySQL DB. 我想统计访问者在某个页面上花费的时间并将其存储在我的MySQL数据库中。

I thought of just starting a timer on window.onload like this: 我想到只是在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);
}

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: 访客离开页面(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();
}

The problem is it doesn't always work. 问题是它并不总是有效。 I would say it works like 3 out of 10 times I try it. 我会说,每尝试10次,它的效果就好像有3次。 Could it be that there is not enough time left for the PHP and SQL to be completely executed? 可能没有足够的时间来完全执行PHP和SQL吗?

As onunload is unreliable, I would consider an AJAX solution. 由于onunload不可靠,因此我考虑使用AJAX解决方案。 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. 我不会每秒对您的计数器执行一次ping操作,因为这似乎过多。

I would break down times into durations you care about, such as <5 seconds, <30 seconds, <1 minute, <5 minutes, 5+ minutes. 我会将时间分解为您关心的持续时间,例如<5秒,<30秒,<1分钟,<5分钟,5分钟以上。

Then I would write a piece of JavaScript like so: 然后,我将像这样编写一段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();

}

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. 诀窍是,每发送一次count.php?q=30 ,您就需要从count.php?q=5计数中减去一个。 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: 为了澄清,如果您有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

Make sense? 说得通?

by the time the onload handler runs, the page is in the process of being taken down. 到onload处理程序运行时,该页面已被删除。 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. 我不确定您对DOM或其​​他服务的状态有什么保证,但是您当然不能期望触发异常事件或超时。 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. 尽管不是标准的一部分,但今天的大多数(全部?)浏览器都提供了一个onbeforeunload钩子,该钩子与其他事件处理程序的工作方式几乎相同,不同之处在于,如果您选择还可以返回false来取消卸载。

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). 您可以按照建议设置一个计时器,该计时器可以根据需要运行(几秒钟左右),然后将信息存储在一个或多个cookie中。 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). 因此,不要将其发送到服务器上的每个周期,仅在您要使用该信息做某事时才将其发送到服务器(例如向用户显示他在您网站上各个页面上花费的所有时间的列表)。

Another option is to save the data in localStorage/cookie and send it the next time that the user comes to the website; 另一种选择是将数据保存在localStorage / cookie中,并在用户下次访问网站时将其发送; 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. 最好使用Date() calc而不是setInterval/setTimeout来计算时间。
It's better to use addEventListener instead of onload = func . 最好使用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);
    }
}

onunload is not a reliable way to track ... well, pretty much anything. onunload并不是一种可靠的跟踪方式...好吧,几乎所有东西。 for example see http://www.google.com/support/forum/p/Google+Analytics/thread?tid=6496cd5d4b627c0e&hl=en 例如,请参阅http://www.google.com/support/forum/p/Google+Analytics/thread?tid=6496cd5d4b627c0e&hl=zh-CN

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. 实际上,诸如Google Analytics(分析)之类的Analytics(分析)系统仅假设用户离开了最后一页。 Ie 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. 但是,如果没有随后的打击发生,那意味着我离开了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM