如何衡量在页面上花费的时间?

How to measure a time spent on a page?

提问人:Richard Knop 提问时间:1/12/2011 更新时间:9/21/2023 访问量:110710

问:

我想测量用户在页面上花费的时间(以整数表示的秒或浮点数的分钟)。我知道当他们离开页面时,我可以触发一个卸载事件。但是如何获得他们已经在那里度过的时间呢?

JavaScript jQuery

评论

4赞 meandmax 3/28/2019
我编写了这个库来测量页面上的活动时间,并在活动时间达到限制时执行某些事情。检查一下,非常欢迎反馈。github.com/atlassian/browser-interaction-timeatlassian.github.io/browser-interaction-time

答:

6赞 heximal 1/12/2011 #1

我认为最好的方法是将时间存储在 onload 中,并在 cookie 中卸载事件处理程序,然后在服务器端脚本中分析它们

评论

0赞 dvhh 1/12/2011
可能是最好的方法,另一种方法是拥有 JavaScript 心跳。
5赞 Jordan Reiter 4/20/2013
仅当用户随后转到服务器上的另一个页面时,这才有效。
9赞 David Hedlund 1/12/2011 #2

我想说的是,最好的办法是跟踪服务器上每个会话 ID 的请求时间。用户在最后一页花费的时间是当前请求的时间与前一个请求的时间之间的差值。

这不会捕获用户访问的最后一个页面(即当没有其他请求时),但我仍然会采用这种方法,否则您必须在 提交请求,这将非常容易出错。onunload

评论

0赞 GrvTyagi 2/29/2016
我想用这种方法跟踪video_page,但问题是当用户在新选项卡中打开新页面时,在这种情况下,middel-ware 检测到新的 url 命中。并从会话中获取时间并点击数据库,但用户只是打开新选项卡而不是关闭当前选项卡并timeme.js并且 ajax 方法对我来说是错误的:(
0赞 GrvTyagi 2/29/2016
现在我认为 ajax 调用每 30 秒一次。 是否对我有用?
43赞 Kevin Dolan 1/12/2011 #3

如果您使用 Google Analytics,他们会提供此统计数据,尽管我不确定他们是如何获得的。

如果你想滚动自己的,你需要有一些AJAX请求被发送到你的服务器进行日志记录。

jQuery有一个.unload(...)方法,你可以这样使用:

$(document).ready(function() {
  var start = new Date();

  $(window).unload(function() {
      var end = new Date();
      $.ajax({ 
        url: "log.php",
        data: {'timeSpent': end - start},
        async: false
      })
   });
});

在此处查看更多信息:http://api.jquery.com/unload/

这里唯一需要注意的是,它使用了 javascript 的 beforeunload 事件,它并不总是有足够的时间发出这样的 AJAX 请求,因此合理地你会丢失大量数据。

另一种方法是定期轮询服务器,发送某种类型的“STILL HERE”消息,这些消息可以更一致地处理,但显然成本更高。

评论

3赞 Rob Fox 3/8/2012
在用户第一次加载 JS 时发送请求,在用户离开页面时发送另一个请求可能是一个更好的主意。这样你就不能进行任何形式的黑客攻击。
1赞 jdepypere 6/28/2013
更好的方法是设置一个带有日期的变量,仅在服务器收到更新信号时才更新。由于 -var 是在页面加载时设置的,因此它永远不会太多。$_SESSION$_SESSION
0赞 user217562 7/17/2017
添加到上面的 ajax 调用中以避免间歇性触发。async : false
0赞 dodov 7/30/2017
您正在全局范围内泄漏变量。end
75赞 jason.zissman 2/1/2015 #4

公认的答案是好的,但是(作为替代方案)我已经在一个小的JavaScript库中做了一些工作,该库可以计算用户在网页上的时间。它还有一个额外的好处,即更准确地(尽管并不完美)跟踪用户实际与页面交互的时间。它忽略了用户切换到不同选项卡、空闲、最小化浏览器等时间。接受的答案中建议的 Google Analytics 方法有一个缺点(据我所知),它只检查您的域何时处理新请求。它将以前的请求时间与新的请求时间进行比较,并将其称为“在网页上花费的时间”。它实际上不知道是否有人正在查看您的页面,是否最小化了浏览器,自上次加载您的页面以来已将选项卡切换到 3 个不同的网页等。

编辑:我更新了示例以包含当前的 API 用法。

编辑 2:更新托管项目的域

https://github.com/jasonzissman/TimeMe.js/

其用法示例:

在您的页面中包括:

<!-- Download library from https://github.com/jasonzissman/TimeMe.js/ -->
<script src="timeme.js"></script>
<script type="text/javascript">
TimeMe.initialize({
    currentPageName: "home-page", // page name
    idleTimeoutInSeconds: 15 // time before user considered idle
});
</script>

如果您想自己向后端报告时间:

xmlhttp=new XMLHttpRequest();
xmlhttp.open("POST","ENTER_URL_HERE",true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
var timeSpentOnPage = TimeMe.getTimeOnCurrentPageInSeconds();
xmlhttp.send(timeSpentOnPage);

TimeMe.js还支持通过 websocket 发送计时数据,因此您不必尝试将完整的 HTTP 请求强制发送到事件中。document.onbeforeunload

评论

1赞 user1111929 4/22/2016
我真的很喜欢你的剧本!不过,在演示页面上,它有一个奇怪的行为:当我切换到另一个窗口(浏览器没有最小化,只是另一个窗口有焦点)时,计时器会继续运行。这是故意的吗?
1赞 jason.zissman 4/23/2016
谢谢!该库依靠浏览器事件来告知它用户何时获得/失去焦点。我假设浏览器在切换窗口时不会报告“失焦”事件,因为拥有多个显示器的用户可能仍然能够看到浏览器。只是一个想法!
1赞 Sherwood Botsford 2/10/2017
获取此指标的另一种方法是检测鼠标移动和滚动/页面移动、按键。这是一种不同的措施。在多屏设置中,我经常在一台显示器上有一个参考页面,而在另一台显示器上工作。参考页面不应该算作页面上的时间,因为它在那里,但我没有看它。经过一些校准,我怀疑每 X 秒一次鼠标移动/按键/滚动事件将指示页面上的时间。我认为 X 在 10 到 30 秒之间。
2赞 jason.zissman 2/24/2017
好主意。TimeMe 实际上正是这样做的——如果你在可配置的时间内处于“空闲”状态(没有鼠标移动、按键等),那么它就会停止跟踪你的时间。实际上,我在上个月左右更新了TimeMe.js,以重新设计很多内部逻辑。原始注释暗示在聚焦新窗口时计时器仍在运行,这似乎不再是问题。
3赞 Joseph Coco 8/2/2017
TimeMe 不会捕获在 iframe 中花费的时间(iframe 也没有运行 TimeMe)。
2赞 Filip Koblański 10/5/2016 #5

根据正确的答案,我认为这不是最好的解决方案。因为根据 jQuery 文档:

卸载事件的确切处理方式因版本而异 浏览器的版本。例如,某些版本的 Firefox 会触发 事件,但当链接被关注时,而不是在窗口关闭时。在 实际使用,行为应在所有支持的浏览器上进行测试 并与类似的 beforeunload 事件形成对比。

另一件事是,您不应该在文档加载后使用它,因为时间减去的结果可能是假的。

因此,更好的解决方案是将其添加到本节末尾的事件中,如下所示:onbeforeunload<head>

<script>
var startTime = (new Date()).getTime();

window.onbeforeunload = function (event) {
    var timeSpent = (new Date()).getTime() - startTime,
        xmlhttp= new XMLHttpRequest();
    xmlhttp.open("POST", "your_url");
    xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    var timeSpentOnPage = TimeMe.getTimeOnCurrentPageInSeconds();
    xmlhttp.send(timeSpent);
};
</script>

当然,如果您想使用空闲检测器计算时间,您可以使用:

https://github.com/serkanyersen/ifvisible.js/

TimeMe 是我粘贴在上面的包的包装器。

17赞 martpie 1/5/2017 #6

除了 Jason 的回答之外,这里有一小段代码,如果您不想使用库,它应该可以解决问题,它会考虑用户何时切换选项卡或聚焦另一个窗口。

let startDate = new Date();
let elapsedTime = 0;

const focus = function() {
    startDate = new Date();
};

const blur = function() {
    const endDate = new Date();
    const spentTime = endDate.getTime() - startDate.getTime();
    elapsedTime += spentTime;
};

const beforeunload = function() {
    const endDate = new Date();
    const spentTime = endDate.getTime() - startDate.getTime();
    elapsedTime += spentTime;

    // elapsedTime contains the time spent on page in milliseconds
};

window.addEventListener('focus', focus);
window.addEventListener('blur', blur);
window.addEventListener('beforeunload', beforeunload);

评论

0赞 kabrice 2/19/2017
是否考虑了空闲时间?
0赞 martpie 4/11/2017
@kabrice这取决于你所说的“空闲”是什么意思。如果选项卡处于聚焦状态,则无法知道用户是否正在阅读您的页面。此代码片段基本上获取用户关注当前选项卡的时间。这意味着当您的选项卡位于前台时。
0赞 babiro 2/17/2017 #7
<body onLoad="myFunction()">
<script src="jquery.min.js"></script>
<script>
var arr = [];
window.onbeforeunload = function(){
var d = new Date();
var n = d.getTime();
arr.push(n);
var diff= n-arr[0];
var sec = diff/1000;
var r = Math.round(sec);
return "Time spent on page: "+r+" seconds";
};
function myFunction() {
var d = new Date();
var n = d.getTime();
arr.push(n);
}
</script>
10赞 Jack G 7/7/2019 #8

𝗽𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲.𝗻𝗼𝘄()

运行内联代码以获取用户到达页面的时间会阻止页面的加载。相反,请使用 which 显示自用户首次导航到页面以来经过了多少毫秒。但是,由于时间重新同步和闰秒等因素,时钟时间可能与导航时间相差一秒或更多。 在 IE10+ 和所有常青浏览器中受支持(evergreen=为娱乐而生,不以营利为目的)。今天仍然存在的最早的Internet Explorer版本是Internet Explorer 11(最后一个版本),因为Microsoft在2014年停止了Windows XPperformance.now()Date.nowperformance.now()

(function(){"use strict";

var secondsSpentElement = document.getElementById("seconds-spent");
var millisecondsSpentElement = document.getElementById("milliseconds-spent");

requestAnimationFrame(function updateTimeSpent(){
    var timeNow = performance.now();
    
    secondsSpentElement.value = round(timeNow/1000);
    millisecondsSpentElement.value = round(timeNow);
    
    requestAnimationFrame(updateTimeSpent);
});
var performance = window.performance, round = Math.round;
})();
Seconds spent on page:&nbsp; <input id="seconds-spent" size="6" readonly="" /><br />
Milliseconds spent here: <input id="milliseconds-spent" size="6" readonly="" />

0赞 KevinHJ 4/12/2021 #9

我发现使用 beforeunload 事件是不可靠的,实际上经常失败。通常,在发送请求之前,页面已被销毁,并且您会收到“网络故障”错误。

正如其他人所说,没有万无一失的方法可以判断用户在页面上停留了多长时间。但是,您可以发送一些线索。

点击和滚动是相当公平的指标,表明有人正在积极查看该页面。我建议侦听点击和滚动事件,并在触发请求时发送请求,尽管频率不超过每 30 或 60 秒一次。

人们可以在计算中使用一些智能,例如,如果每 30 秒左右触发一个事件,持续 5 分钟,那么 30 分钟内没有事件,然后再触发几个事件,很有可能,用户在 30 分钟内喝咖啡。

let sessionid;

function utilize(action) {
  // This just gets the data on the server, all the calculation is done server-side.

  let href = window.location.href;
  let timestamp = Date.now();
  sessionid = sessionid || timestamp;

  let formData = new FormData();
  formData.append('sessionid', sessionid);
  formData.append('timestamp', timestamp);
  formData.append('href', href);
  formData.append('action', action || "");

  let url = "/php/track.php";

  let response = fetch(url, {
    method: "POST",
    body: formData
  });
}

let inhibitCall = false;

function onEvent() {
  // Don't allow an update any more often than every 30 seconds.

  if (!inhibitCall) {
    inhibitCall = true;
    utilize('update');
    setTimeout(() => {
      inhibitCall = false;
    }, 30000);
  }
}

window.addEventListener("scroll", onEvent);
window.addEventListener("click", onEvent);

utilize("open");
0赞 Observer 3/25/2023 #10

我想我已经找到了一个简单的解决方案。

只需使用重复间隔和 LocalStorage 以及一些 PHP(或任何服务器端语言)

设置 LocalStorage

if (typeof localStorage.getItem("time_spent_current") === "undefined" || localStorage.getItem("time_spent_current") == null) {
    localStorage.setItem("time_spent_current", -5);
}

每 5 秒向本地存储添加一次时间

var intervalId = window.setInterval(function(){
    // call your function here
    var val = localStorage.getItem("time_spent_current");
    var time_spent_current = parseInt(val) + 5;
    localStorage.setItem("time_spent_current", time_spent_current);
    console.log(time_spent_current);
}, 5000);

如果要将数据更新到服务器,请上传数据 每个新会话。

<?php   if(!isset($_SESSION["time_spent_current"])){    ?>
    $(document).ready(function() {
      var start = new Date();

      $(window).unload(function() {
          var end = new Date();
          $.ajax({ 
            url: "log.php",
            data: {'timeSpent': time_spent_current},
            async: false
          })
       });
    });
<?php
    $_SESSION["time_spent_current"] = 1;
        }   
?>

P.s.:使用Kevin Dolan的脚本(最初接受的答案)将数据上传到服务器

2赞 Eugeny K 8/17/2023 #11

得票最多的答案都不适合我。因为有可能在不转移焦点的情况下打开页面(例如,使用鼠标中键或“Ctrl”+左键组合时)。

TimeMe.js有一个可怕的缺点。当您在新选项卡中打开链接时,它不会获得焦点(“Ctrl”+ 左键单击链接),库会启动该选项卡的活动计数器,就好像该选项卡处于焦点中一样。TimeMe.js仅在用户激活然后离开选项卡时停止计数器(该操作会触发窗口的模糊事件)。

我建议大家看看页面可见性API

在我看来,它提供了一个简单可靠的解决方案来检查选项卡是否实际处于活动状态(或在屏幕上可见)。

document.addEventListener("visibilitychange", () => {
  if (document.hidden) {
    // The tab is in the focus.
    // Let's measure a user time on the page
  } else {
    // Tab is not in the focus
    // Stop the counter
  }
});

否则,您可以将条件放在时间间隔循环中:document.hidden

let elapsedTime = 0;

setInterval(function() {
    

   if (!document.hidden) {
    elapsedTime += 1;

    // Send to server or do other stuff

    console.log(elapsedTime);
   }

  
}, 1000);