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模仿msgbox提示效果代码
Jun 10 Javascript
Whatever:hover 无需javascript让IE支持丰富伪类
Jun 29 Javascript
jquery dialog键盘事件代码
Aug 01 Javascript
jquery星级插件、支持页面中多次使用
Mar 25 Javascript
jquery 插件学习(五)
Aug 06 Javascript
js检验密码强度(低中高)附图
Jun 05 Javascript
node.js中的fs.unlink方法使用说明
Dec 15 Javascript
Java与JavaScript中判断两字符串是否相等的区别
Mar 13 Javascript
浅谈JS中的常用选择器及属性、方法的调用
Jul 28 Javascript
ng-events类似ionic中Events的angular全局事件
Sep 05 Javascript
Windows上node.js的多版本管理工具用法实例分析
Nov 06 Javascript
ant design 日期格式化的实现
Oct 27 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 fopen 读取带中文URL地址的一点见解
2012/09/25 PHP
zf框架的校验器InArray使用示例
2014/03/13 PHP
php正则preg_replace_callback函数用法实例
2015/06/01 PHP
Laravel使用Caching缓存数据减轻数据库查询压力的方法
2016/03/15 PHP
PHP 常用时间函数资料整理
2016/10/22 PHP
PHP实现简易图形计算器
2020/08/28 PHP
Nigma vs AM BO3 第一场2.13
2021/03/10 DOTA
页面版文本框智能提示JS代码
2009/11/20 Javascript
Android中资源文件(非代码部分)的使用概览
2012/12/18 Javascript
jQuery实现的指纹扫描效果实例(附演示与demo源码下载)
2016/01/26 Javascript
关于获取DIV内部内容报错的原因分析及解决办法
2016/01/29 Javascript
基于BootStrap Metronic开发框架经验小结【二】列表分页处理和插件JSTree的使用
2016/05/12 Javascript
JS公共小方法之判断对象是否为domElement的实例
2016/11/25 Javascript
详解在Angularjs中ui-sref和$state.go如何传递参数
2017/04/24 Javascript
nodejs实现连接mongodb数据库的方法示例
2018/03/15 NodeJs
JavaScript使用小插件实现倒计时的方法讲解
2019/03/11 Javascript
Vue拖拽组件列表实现动态页面配置功能
2019/06/17 Javascript
[01:11:21]DOTA2-DPC中国联赛 正赛 Phoenix vs CDEC BO3 第三场 3月7日
2021/03/11 DOTA
python中实现指定时间调用函数示例代码
2017/09/08 Python
Python 3.x 判断 dict 是否包含某键值的实例讲解
2018/07/06 Python
在Python dataframe中出生日期转化为年龄的实现方法
2018/10/20 Python
Python2与Python3的区别实例总结
2019/04/17 Python
使用python-opencv读取视频,计算视频总帧数及FPS的实现
2019/12/10 Python
python实现ftp文件传输功能
2020/03/20 Python
Django CBV模型源码运行流程详解
2020/08/17 Python
Python安装并操作redis实现流程详解
2020/10/13 Python
python网络爬虫实现发送短信验证码的方法
2021/02/25 Python
CSS3 实现footer 固定在底部(无论页面多高始终在底部)
2019/10/15 HTML / CSS
Airbnb爱彼迎官网:成为爱彼迎房东,赚取收入
2019/03/14 全球购物
法国最大的在线眼镜店:EasyLunettes
2019/08/26 全球购物
Nobody Denim官网:购买高级女士牛仔裤
2021/03/15 全球购物
团队精神演讲稿
2013/12/31 职场文书
《识字五》教学反思
2014/03/01 职场文书
2015年文明创建工作总结
2015/04/30 职场文书
Python中else的三种使用场景
2021/06/16 Python
Java 垃圾回收超详细讲解记忆集和卡表
2022/04/08 Java/Android