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 相关文章推荐
jquery实用代码片段集合
Aug 12 Javascript
详解AngularJS Filter(过滤器)用法
Dec 28 Javascript
JavaScript通过代码调用Flash显示的方法
Feb 02 Javascript
jQuery 获取多选框的值及多选框中文的函数
May 16 Javascript
极力推荐一款小巧玲珑的可视化编辑器bootstrap-wysiwyg
May 27 Javascript
Node.js调用fs.renameSync报错(Error: EXDEV, cross-device link not permitted)
Dec 27 Javascript
webpack4.x开发环境配置详解
Aug 04 Javascript
微信小程序实现购物车代码实例详解
Aug 29 Javascript
js前端如何写一个精确的倒计时代码
Oct 25 Javascript
JavaScript将数组转换为链表的方法
Feb 16 Javascript
如何基于layui的laytpl实现数据绑定的示例代码
Apr 10 Javascript
基于Element封装一个表格组件tableList的使用方法
Jun 29 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简单截取字符串代码示例
2016/10/19 PHP
php获取开始与结束日期之间所有日期的方法
2016/11/29 PHP
PHP 进度条函数的简单实例
2017/09/19 PHP
php图片合成方法(多张图片合成一张)
2017/11/25 PHP
PHP结合jquery ajax实现上传多张图片,并限制图片大小操作示例
2019/03/01 PHP
使用JavaScript实现Java的List功能(实例讲解)
2013/11/07 Javascript
从JQuery源码分析JavaScript函数的apply方法与call方法
2014/09/25 Javascript
dreamweaver 8实现Jquery自动提示
2014/12/04 Javascript
VS2008中使用JavaScript调用WebServices
2014/12/18 Javascript
微信内置浏览器私有接口WeixinJSBridge介绍
2015/05/25 Javascript
javascript HTML+CSS实现经典橙色导航菜单
2016/02/16 Javascript
使用jquery.qrcode.js生成二维码插件
2016/10/17 Javascript
简单实现js浮动框
2016/12/13 Javascript
React通过父组件传递类名给子组件的实现方法
2017/11/13 Javascript
微信小程序自定义导航栏(模板化)
2019/11/15 Javascript
package.json各个属性说明详解
2020/03/11 Javascript
用js编写留言板
2020/03/17 Javascript
Ant Design Vue table中列超长显示...并加提示语的实例
2020/10/31 Javascript
[47:06]DOTA2上海特级锦标赛主赛事日 - 4 败者组第五轮 MVP.Phx VS EG第一局
2016/03/05 DOTA
Python、Javascript中的闭包比较
2015/02/04 Python
在Python中操作字典之clear()方法的使用
2015/05/21 Python
Python中方法链的使用方法
2016/02/23 Python
Python的Flask框架中集成CKeditor富文本编辑器的教程
2016/06/13 Python
python3 与python2 异常处理的区别与联系
2016/06/19 Python
Python3 Random模块代码详解
2017/12/04 Python
Python 实现一行输入多个值的方法
2018/04/21 Python
python 实现将字典dict、列表list中的中文正常显示方法
2018/07/06 Python
python实现通过队列完成进程间的多任务功能示例
2019/10/28 Python
python创建子类的方法分析
2019/11/28 Python
Python如何存储数据到json文件
2020/03/09 Python
Shell编程面试题
2016/05/29 面试题
五一活动标语
2014/06/30 职场文书
应聘教师求职信范文
2015/03/20 职场文书
2016年最美孝心少年事迹材料
2016/02/26 职场文书
如何做好工作总结!
2019/04/10 职场文书
Redis三种集群模式详解
2021/10/05 Redis