前端开发入门到实战:计算一个页面内每个模块的曝光时间(停留时间)

产品希望看到投放出去的活动页,用户对其页面内的什么信息比较感兴趣,对什么信息完全不感兴趣。=> 计算页面内每模块的停留时间

第一次听到这个需求,我的大脑开始疯狂运转,然后想到了plan 1, plan 2, plan3...中间还有很多失败想法我已经忘记了,这里方案三是我最终采用的方法。

方案一:根据页面dom将页面分模块

var bodyChildrenLists = $(‘body‘).children()
var bodyChildDomLsit = []
var initHeight = 0
for (var i = 0; i < bodyChildrenLists.length; i++) {
    if (bodyChildrenLists[i].tagName !== ‘SCRIPT‘) {
        bodyChildDomLsit.push({
        className: bodyChildrenLists[i].className,
        height: bodyChildrenLists[i].offsetHeight
      })
    }
}

存在的问题:?
不同人的代码风格差异性大,该方案不适合这类代码风格

<body>
    <div class="container">
        <div class="header"></div>
        <div class="nav"></div>
        <div class="footer"></div>
    </div>
</body>

这种方式很好,就是,,,如果大家的代码风格很一致的情况下使用比较好。

方案二:计算出用户打开页面后的所有行为

var scrollTop = 0
var time = Date.now()
window._stayStatus = {
// 记录运动轨迹, down > 1 向下移动 down 向上移动, sliderDis 移动距离, time 移动耗时, initDis 初始距离, initTime 初始时间
    moveData: [],
    enterTime: Date.now()
}
var moveData = window._stayStatus.moveData
var currentMoveIndex = 0
function move () {
    var currentTime = Date.now()
    var currentScrollTop = $(window).scrollTop()
    var dis = currentScrollTop - scrollTop
    var disTime = currentTime - time
    // 上一次滑动页面和这次滑动页面的时间差大于100ms,就视作用户在某一个段时间做了停留
    if (disTime > 100) {
        if (moveData[currentMoveIndex] && moveData[currentMoveIndex].down === 0) {
            moveData[currentMoveIndex].time += disTime
        } else {
            moveData.push({
                down: 0,
                initTime: time, // initTime表示进入该状态的初始时间
                initDis: currentScrollTop, //initDis 表示进入该状态的初始位置
                sliderDis: dis, // 在该状态内滑动的距离
                time: disTime // 在该状态经历的时间(ms)
            })
        }
    } else {
        // 向下滑动
        if (dis >= 0) {
            // 如果之前已经是向下滑动的状态,只需要在原来的数据上累加滑动距离和滑动时间
            if (moveData[currentMoveIndex] && moveData[currentMoveIndex].down > 0) {
                moveData[currentMoveIndex].sliderDis += dis
                moveData[currentMoveIndex].time += disTime
            } else {
                moveData.push({
                    down: 1,
                    initTime: currentTime,
                    initDis: currentScrollTop,
                    sliderDis: dis,
                    time: disTime
                })
            }
        } else {
            if (moveData[currentMoveIndex] && moveData[currentMoveIndex].down < 0) {
                moveData[currentMoveIndex].sliderDis += dis
                moveData[currentMoveIndex].time += disTime
            } else {
                moveData.push({
                    down: -1,
                    initTime: currentTime,
                    initDis: currentScrollTop,
                    sliderDis: dis,
                    time: disTime
                })
            }
        }
    }
    currentMoveIndex = moveData.length - 1
    time = currentTime
    scrollTop = currentScrollTop
  }
  window.onscroll = function (e) {
    move()
  }

根据以上方法获取到的数据如下:

表示:用户在距顶部2px时停留了2728ms后,向下滑动了612px,滑动时间为595ms,然后又在距顶部612px停留了8649ms,最后向上滑动了604px,经历了167ms。

存在的问题:
最后得到的数据量虽然不会很大,但是将这样的数据给数据组分析,存在一定的难度。这是在没有和产品对接时,自己想的办法,有点想复杂了。但是这种方式可以比较生动模拟出用户的行为。

方案三: 固定模块尺寸,计算每模块的停留时间

与我的产品对接了基本的规定:

  • 每1300px高度作为为一个模块,进行埋点统计。
  • 每屏曝光范围大于400px时作为有效曝光,开始记录时长。
  • 每一模块的上报时间均为在模块内滑动时间及静止停留时间加和。
  • 设当前模块为模块0,当用户未到达模块1时,反复滑动时间均记做模块0内时间。
  • 设当前模块为模块0,当用户到达模块1后又通过滑动行为返回模块0,此时会重新记录一次模块0数据
  • 最后一次上报时间为用户离开该页面(进入下一流程页面或关闭浏览器),需统计能够监测到的用户离开行为及场景。

根据以上需求,我做了一个小demo,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="//lib04.xesimg.com/lib/jQuery/1.11.1/jquery.min.js"> </script>
  <script src="//zt.xueersi.com/apStatic/js/qz-rem.js"></script>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    .section {
      height: 1300px;
      border-bottom: 1px solid #f00;
      box-sizing: border-box;
      padding-top: 400px;
    }
  </style>
</head>
<body>
  <div id="app">
    <div>
      <p>停留时间0:</p>
      <p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 0">{{0}}  ----  {{item.time}}</p>
    </div>
    <div>
      <p>停留时间1:</p>
      <p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 1">{{1}}  ----  {{item.time}}</p>
    </div>
    <div>
      <p>停留时间2:</p>
      <p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 2">{{2}}  ----  {{item.time}}</p>
    </div>
    <div>
      <p>停留时间3:</p>
      <p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 3">{{3}}  ----  {{item.time}}</p>
    </div>
    <div>
      <p>停留时间4:</p>
      <p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 4">{{4}}  ----  {{item.time}}</p>
    </div>
    <div>
      <p>停留时间5:</p>
      <p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 5">{{5}}  ----  {{item.time}}</p>
    </div>
    <div>
      <p>停留时间6:</p>
      <p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 6">{{6}}  ----  {{item.time}}</p>
    </div>
  </div>
  <script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>
  <script>
    new Vue({
      el: ‘#app‘,
      data: {
        movedata: [],
        scrollTop: $(window).scrollTop(),
        time: Date.now(),
        stayTime: 0
      },
      mounted () {
        // 部分页面存在页面滚动到某一高度时,刷新后页面也会固定在该高度的问题,初始化movedata数据
        var index = parseInt(this.scrollTop / 1300) + 1
        for (var i = 0; i <= index; i++) {
          this.movedata.push({
            pos: i * 1300,
            time: 0
          })
        }
        window.onscroll = () => {
          this.scrollTop = $(window).scrollTop()
        }
        setInterval(() => {
          var currentTime = Date.now()
          var disTime = currentTime - this.time
          // 计算当前是展现在屏幕中的模块序号,一个屏幕内最多展现两个模块,currentIndex永远指定的是上面的模块
          var currentIndex = parseInt(this.scrollTop / 1300)
          var length = this.movedata.length
          if (currentIndex + 1 >= length) {
            for (var i = length; i <= currentIndex + 1; i++) {
              this.movedata.push({
                pos: 1300 * i,
                time: disTime
              })
            }
          } else {
            // 计算当前的滚动高度超出满屏的多少
            var modeDis = this.scrollTop - this.movedata[currentIndex].pos
            // 表示一屏中上面的模块展示区域超过400,可以累加停留时间
            if ((1300 - modeDis) > 400) {
              this.movedata[currentIndex].time += disTime
            }
            // 表示一屏中下面的模块展示区域超过400,可以累加停留时间
            if (modeDis > 400) {
              this.movedata[currentIndex + 1].time += disTime
            }
          }
          this.time = currentTime
        }, 1000)
      }
    })
  </script>
</body>
</html>

使用这种方式,movedata的数组长度等于页面内的模块个数

这里推荐一下我的前端学习交流圈:767273102 ,里面都是学习前端的从最基础的HTML+CSS+JS【炫酷特效,游戏,插件封装,设计模式】到移动端HTML5的项目实战的学习资料都有整理,送给每一位前端小伙伴。2019最新技术,与企业需求同步。好友都在里面学习交流,每天都会有大牛定时讲解前端技术!

学习前端,我们是认真的

原文地址:https://blog.51cto.com/14284898/2404481

时间: 2024-11-05 18:41:30

前端开发入门到实战:计算一个页面内每个模块的曝光时间(停留时间)的相关文章

前端开发入门到实战:CSS 页面滚动平滑

1. 这些年自己步子慢了 这些年自己在无障碍访问,SVG和Canvas这些偏小众的领域花了大量的学习精力,以至于很多前端新特性,新技术没能及时关注和了解,有CSS3领域的新属性,有JS领域的新API,包括全新的ES6/ES7语法等,相比以前的学习,步子确实慢了.比方说,本文要介绍的平滑滚动,无论是CSS还是JS,现代浏览器都提供了原生的属性或方法支持,而且差不多已经有一年时间,而我最近才知道,和数年前实时关注新技术的自己形成了明显的对比. 不过随着SVG和Canvas的基础越来越牢固,细节越来越

前端开发入门到实战:通过 rem 和 vw 实现页面等比例缩放自适应

一.rem 和 vw 简介 1. rem rem?是相对长度单位,是指相对于根元素(即html元素)font-size(字号大小)的倍数. 浏览器支持:Caniuse 示例 若根元素?font-size?为 12px html { font-size: 12px; } h1 { font-size: 2rem; /* 2 × 12px = 24px */ } p { font-size: 1.5rem; /* 1.5 × 12px = 18px */ } div { width: 10rem;

Python开发入门与实战2-第一个Django项目

2.第一个Django项目 上一章节我们完成了python,django和数据库等运行环境的安装,现在我们来创建第一个django project吧,迈出使用django开发应用的第一步. 2.1.创建第一个Django项目 我们创建一个我们存放Django的工作目录,示例:C:\My Files\Python Projects 在命令提示符窗口进入到刚才创建的目录,运行运行命令: django-admin.py startproject mysite 这样会在你的当前目录下创建一个目录mysi

前端开发入门到实战:HTML5新增和废弃的标签

一.废弃的标签 以下的 HTML 4.01 元素在HTML5中已经被删除,虽然浏览器为了兼容性考虑都还支持这些标签,但建议使用新的替代标签,矛盾的是老浏览器对新标签的支持度又不够,视项目的受众对象而定了. 1.能用CSS代替的元素 这些元素包含basefont.big.center.font.s.strike.tt.u.这些元素纯粹是为页面展示用的,表现的内容应该由CSS完成. 2.frame框架 这些元素包含frameset.frame.noframes.HTML5中不支持frame框架,只支

前端开发入门到实战:把HTML转成PDF的4个方案及实现

在本文中,我将展示如何使用 Node.js.Puppeteer.headless Chrome 和 Docker 从样式复杂的 React 页面生成 PDF 文档. 背景:几个月前,一个客户要求我们开发一个功能,用户可以得到 PDF 格式的 React 页面内容.该页面基本上是患者病例的报告和数据可视化结果,其中包含许多 SVG.另外还有一些特殊的请求来操纵布局,并对 HTML 元素进行一些重新排列.因此与原始的 React 页面相比,PDF 中应该有不同的样式和额外的内容. 由于这个任务比用简

前端开发入门到实战:css实现div垂直水平居中的2种常用方法

方法一: 利用vertical-align:middle进行垂直方向上的居中对齐,此方法需要满足的条件: 设置父元素的行高line-height等于父元素height的高度 子元素必须是行内块级元素display:inline-block; 子元素设置vertical-align:middle 此方法在开发中不能右浮动(不能靠右边) 下方是完整代码,可以新建一个HTML文件进行测试(绿色的盒子): <html> <head> <title>导航条</title&g

Python开发入门与实战3-Django动态页面

3.Django动态页面 上一章节我们实现的helloworld视图是用来演示Django网页是创建的,它不是一个动态网页,每次运行/helloworld/,我们都将看到相同的内容,它类似一个静态HTML文件. 接下来我们将实现另一个视图,加入动态内容,例如当前日期和时间显示在网页上.通过简单的下一步,来演示Django的这个技术. 3.1.一个简单的动态页面例子 这个视图做两件事情: 获取服务器当前日期和时间,并返回包含这些值的HttpResponse .为了让Django视图显示当前日期和时

前端开发入门到实战:css实现修改浏览器自动填充表单的默认样式

当表单中存在input[password]的时候,采用submit方式提交.就会触发浏览器自动填充表单.比如chrome自动填充后,淡×××输入框代替了背景样式,看起来有些怪异. ?那么如何通过css实现取消浏览器自动填充表单的默认样式呢? 解决方法一: 当input文本框是纯色背景的,可以对input:-webkit-autofill使用足够大的纯色内阴影来覆盖input输入框的×××背景:如: input:-webkit-autofill { -webkit-box-shadow: 0 0

前端开发入门到实战:CSS中字体单位:px、em、rem和%

对于绘图和印刷而言,"单位"相当重要,然而在网页排版里,单位也是同样具有重要性,在CSS3普及以来,更支持了一些方便好用的单位(px.em.rem-等),这篇文章将整理这些常用的CSS单位,也帮助自己未来在使用上能更加得心应手. "网页"和"印刷"的单位若要把单位做区隔,最简单可以分为"网页"和"印刷"两大类,通常对于CSS来说只会应用到网页的样式,毕竟真正要做印刷,还是会倾向透过排版软件来进行设计. 网页