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的调试
Jan 28 Javascript
jquery+ajax请求且带返回值的代码
Aug 12 Javascript
多个js毫秒倒计时同时进行效果
Jan 05 Javascript
JavaScript iframe数据共享接口实现方法
Jan 06 Javascript
JS创建事件的三种方法(实例代码)
May 12 Javascript
AngularJS实现单一页面内设置跳转路由的方法
Jun 28 Javascript
快速解决brew安装特定版本flow的问题
May 17 Javascript
Vue项目实现换肤功能的一种方案分析
Aug 28 Javascript
微信小程序学习总结(二)样式、属性、模板操作分析
Jun 04 Javascript
JavaScript实现复选框全选和取消全选
Nov 20 Javascript
javaScript Array api梳理
Mar 31 Javascript
JS中forEach()、map()、every()、some()和filter()的用法
May 11 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 静态页面中显示动态内容
2009/08/14 PHP
PHP GD 图像处理组件的常用函数总结
2010/04/28 PHP
php的list()的一步操作给一组变量进行赋值的使用
2011/05/18 PHP
深入理解PHP 数组之count 函数
2016/06/13 PHP
jquery 插件 web2.0分格的分页脚本,可用于ajax无刷新分页
2008/12/25 Javascript
Jquery在IE7下无法使用 $.ajax解决方法
2009/11/11 Javascript
jquery中ajax学习笔记一
2011/10/16 Javascript
理解JSON:3分钟课程
2011/10/28 Javascript
深入理解JavaScript中的传值与传引用
2013/12/09 Javascript
js从Cookies里面取值的简单实现
2014/06/30 Javascript
jQuery判断当前点击的是第几个li的代码
2014/09/26 Javascript
JavaScript取得WEB安全颜色列表的方法
2015/07/14 Javascript
React Native第三方平台分享的实例(Android,IOS双平台)
2017/08/04 Javascript
vue监听scroll的坑的解决方法
2017/09/07 Javascript
JS解析后台返回的JSON格式数据实例
2018/08/06 Javascript
微信小程序实现的一键连接wifi功能示例
2019/04/24 Javascript
Vue.js中的组件系统
2019/05/30 Javascript
微信小程序使用canvas自适应屏幕画海报并保存图片功能
2019/07/25 Javascript
全面解析JavaScript Module模式
2020/07/24 Javascript
vue实现打地鼠小游戏
2020/08/21 Javascript
Python爬虫基础之XPath语法与lxml库的用法详解
2018/09/13 Python
对Python3+gdal 读取tiff格式数据的实例讲解
2018/12/04 Python
Python使用requests提交HTTP表单的方法
2018/12/26 Python
python的turtle库使用详解
2019/05/10 Python
解决python执行不输出系统命令弹框的问题
2019/06/24 Python
使用Keras实现Tensor的相乘和相加代码
2020/06/18 Python
德国奢侈品网上商城:Mytheresa
2016/08/24 全球购物
Expedia爱尔兰:酒店、机票、租车及廉价假期
2017/01/02 全球购物
埃弗顿足球俱乐部官方网上商店:Everton Direct
2018/01/13 全球购物
JustFab加拿大:女鞋、靴子、手袋和服装在线
2018/05/18 全球购物
环境科学专业研究生求职信
2013/10/02 职场文书
高三自我鉴定范文
2013/10/19 职场文书
大学生两会学习心得体会
2014/03/10 职场文书
满月酒主持词
2014/03/27 职场文书
出纳年终工作总结2014
2014/12/05 职场文书
劳动保障个人工作总结
2015/03/04 职场文书