js如何实现元素曝光上报


Posted in Javascript onAugust 07, 2019

进行数据上报的时候,经常会遇到列表数据曝光上报的问题,只对在当前可视范围内的数据内容进行曝光上报,而对于未在可视范围内的数据不进行曝光上报,等待用户滚动页面或者区域使元素出现在可视范围内时才进行曝光上报。

解决方案

目前针对此类问题,主要有两种解决方案。

方案一:监听页面或者区域scroll事件,通过getBoundingClientRect接口取元素的位置与可视窗口进行判断。

function isElementInViewport(el) {
  var rect = el.getBoundingClientRect();

  var width_st = rect.width / 2,
    height_st = rect.height / 2;

  var innerHeight = window.innerHeight,
    innerWidth = window.innerWidth;


  if (  rect.top <=0 && rect.height > innerHeight 
    || rect.left <= 0 && rect.width > innerWidth
  ) {
    return rect.left * rect.right <= 0
      || rect.top * rect.bottom <= 0
  }

  return (
      rect.height > 0 
    && rect.width > 0 
    && ( ( rect.top >= 0 && rect.top <= innerHeight - height_st )
      || ( rect.bottom >= height_st && rect.bottom <= innerHeight ) )
    && ( ( rect.left >= 0 && rect.left <= innerWidth - width_st )
      || ( rect.right >= width_st && rect.right <= innerWidth ) )
  );
}

var nodes = document.querySelectorAll(".item")
function report(node) {
  // 上报的逻辑
}
window.onscroll = function() {
  nodes.forEach(node => {
    if( isElementInViewport(node) ) {
      report(node)
    }
  })
  
}

优点:兼容性好

缺点:

  • 需要关注页面或者区域的scroll事件
  • 频繁的scroll事件,性能问题

方案二:通过 IntersectionObserver 监听元素是否处于可视范围

function report(node) {
  // 上报的逻辑
}
var intersectionObserver = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if( entry.intersectionRatio > 0 ) {
      report(entry.target)
    }
  })
})
var nodes = document.querySelectorAll(".item")
nodes.forEach(node => {
  intersectionObserver.observe(node)
})

优点:

  • 无须关注 scroll
  • 回调是异步触发,不会频繁触发,性能好

缺点:兼容性不好?

实际上,针对兼容性问题,w3c 官方提供了对应 polyfill, 因此intersectionObserver用于生产是可行的。

总结

笔者在实际运用中,通过 IntersectionObserver 封装了一个简单的调用库,应用于可视化埋点 sdk 中,用于解决元素曝光问题,如下

require('intersection-observer'); // polyfill

class Exposure {
  constructor(callback) {
    if (!callback || typeof callback !== 'function') {
      throw new Error("need callback or selector param")
      return
    }
    this.intersectionObserver = new IntersectionObserver((entries) => {
      entries.forEach(item => {
        if (item.intersectionRatio > 0) {
          if (item.target) {
            callback(item.target, item)
            this.intersectionObserver.unobserve(item.target)
          }
        }
      })
    });
  }

  observe(selector, ignoreExposured) {
    if (!this.intersectionObserver || !selector) {
      return
    }
    let nodes = []
    if( this.isDOM(selector) ) { // dom节点
      nodes = [selector]
    }else { // 选择器
      nodes = document.querySelectorAll(selector)
    }
    if (!nodes.length) {
      return
    }
    nodes.forEach(node => {
      if (!ignoreExposured && node.__wg__tracker__exposured__) {
        return
      }
      node.__wg__tracker__exposured__ = true
      // 开始观察
      this.intersectionObserver.observe(
        node
      );
    })
  }

  disconnect() {
    if (!this.intersectionObserver) {
      return
    }
    this.intersectionObserver.disconnect()
  }

  isDOM(obj) {
    if( !obj ) {
      return false
    }
    if( typeof HTMLElement === 'object' ) {
      return obj instanceof HTMLElement
    }
    if( typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string' ) {
      return true
    }
    return false
  }
}

export default Exposure

调用方法:

function report() {}
var exposurer = new Exposure((node) => {
  report(node)
})
exposurer.observe(".item)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
阻止JavaScript事件冒泡传递(cancelBubble 、stopPropagation)
May 08 Javascript
来自国外的14个图片放大编辑的jQuery插件整理
Oct 20 Javascript
javaScript实现浮点数转十六进制字符
Oct 29 Javascript
node.js集成百度UE编辑器
Feb 05 Javascript
angularJS结合canvas画图例子
Feb 09 Javascript
jquery实现未经美化的简洁TAB菜单效果
Aug 28 Javascript
JQuery ztree带筛选、异步加载实例讲解
Feb 25 Javascript
jQuery中的each()详细介绍(推荐)
May 25 Javascript
使用BootStrapValidator完成前端输入验证
Sep 28 Javascript
jQuery基本选择器和层次选择器学习使用
Feb 27 Javascript
vueScroll实现移动端下拉刷新、上拉加载
Mar 22 Javascript
原生JS实现拖拽效果
Dec 04 Javascript
详解Element-UI中上传的文件前端处理
Aug 07 #Javascript
element-ui中Table表格省市区合并单元格的方法实现
Aug 07 #Javascript
Vue+Typescript中在Vue上挂载axios使用时报错问题
Aug 07 #Javascript
更优雅的微信小程序骨架屏实现详解
Aug 07 #Javascript
vue 集成jTopo 处理方法
Aug 07 #Javascript
vue 集成 vis-network 实现网络拓扑图的方法
Aug 07 #Javascript
弱类型语言javascript开发中的一些坑实例小结【变量、函数、数组、对象、作用域等】
Aug 07 #Javascript
You might like
PHP+Mysql基于事务处理实现转账功能的方法
2015/07/08 PHP
php禁用函数设置及查看方法详解
2016/07/25 PHP
php魔法函数与魔法常量使用介绍
2017/07/23 PHP
PHP常用函数之格式化时间操作示例
2019/10/21 PHP
Javascript-Mozilla和IE中的一个函数直接量的问题分析
2007/08/12 Javascript
jquery应该如何来设置改变按钮input的onclick事件
2012/12/10 Javascript
Jquery实现图片放大镜效果的思路及代码(自写)
2013/10/18 Javascript
javascript实用小函数使用介绍
2013/11/11 Javascript
JavaScript不使用prototype和new实现继承机制
2014/12/29 Javascript
jquery实现未经美化的简洁TAB菜单效果
2015/08/28 Javascript
javascript表单事件处理方法详解
2016/05/15 Javascript
动态加载js、css的实例代码
2016/05/26 Javascript
Jquery 自定义事件实现发布/订阅的简单实例
2016/06/12 Javascript
快速掌握jQuery插件WebUploader文件上传
2016/11/07 Javascript
原生JS实现的多个彩色小球跟随鼠标移动动画效果示例
2018/02/01 Javascript
vue webpack实用技巧总结
2018/04/24 Javascript
解决axios发送post请求返回400状态码的问题
2018/08/11 Javascript
JS封装的模仿qq右下角消息弹窗功能示例
2018/08/22 Javascript
详解vuex数据传输的两种方式及this.$store undefined的解决办法
2019/08/26 Javascript
nodejs的安装使用与npm的介绍
2019/09/11 NodeJs
Python入门之modf()方法的使用
2015/05/15 Python
Python使用tablib生成excel文件的简单实现方法
2016/03/16 Python
总结python实现父类调用两种方法的不同
2017/01/15 Python
Python OpenCV中的resize()函数的使用
2019/06/20 Python
Python获取好友地区分布及好友性别分布情况代码详解
2019/07/10 Python
Python3之字节串bytes与字节数组bytearray的使用详解
2019/08/27 Python
如何搭建pytorch环境的方法步骤
2020/05/06 Python
如何使用Pytorch搭建模型
2020/10/26 Python
python boto和boto3操作bucket的示例
2020/10/30 Python
HTML5 的新的表单元素(datalist/keygen/output)使用介绍
2013/07/19 HTML / CSS
HTML5实现经典坦克大战坦克乱走还能发出一个子弹
2013/09/02 HTML / CSS
采购部部长岗位职责
2014/02/06 职场文书
2014年高考决心书
2014/03/11 职场文书
竞选班长演讲稿400字
2014/08/22 职场文书
2015年个人现实表现材料
2014/12/10 职场文书
2015年安置帮教工作总结
2015/05/22 职场文书