二、前端数据采集点击坐标怎么获取

一、前言

本文代码需结合前文(前文链接: 前端简单埋点sdk的实现思路),前文阐述了前端实现数据采集sdk的一个简单思路,而最近我得到了一个新的需求:采集用户的点击坐标,后续生成热力图,那就这个需求,我也尝试提供一些实现思路,如果大佬们有更好的方法或者发现我的错误,望指导、指正。

二、思路

根据前文,实现了一个数据采集类。而在sdk加载阶段(_sdk_init_loading),在其中添加了点击事件的监听器,但这个简单实现并不能满足我们的需求。我们需要考虑到一下几个情况:

  1. 单次点击还是双击?
  2. 如果点击的链接,页面跳转前能否准确记录点击跳转的坐标?
  3. 事件的触发顺序?
  4. 事件是否会被阻止?
  5. 是在pc还是移动点击?点击时是横屏还是竖屏?不同尺寸的设备搜集的坐标如何统一?

一般常用的点击事件有clickdbclickmousedownpointdown,而通过实践和经验我们可知道这几个事件的触发顺序为pointdown、mousedown、click、dbclick,并且他们还各有限制,例如mousedown在一些滑动交互的地方可能会被阻止事件,dbclick只在pc端浏览器被触发,在一些a标签点击跳转时,click不会触发…通过了解这些事件的特性,我们可以知道如何才能在不同情况下获取点击坐标。而对于最后搜集的坐标统一性,我们需要根据一个屏幕标准对坐标进行换算。

因此我的实现思路大致整理如下:

  1. 根据一个设备标准,实现一个坐标换算的方法coordConvert;
  2. 单独实现点击事件的处理方法clickHandler;
  3. 初始化点击事件列表clickEventList,将点击事件需要获取的信息全部push到其中;
  4. 页面卸载前的点击坐标无法通过click事件获取,而mousedown可能又会被banner等滑动性质的元素阻止,所以此处的坐标通过pointdown触发获取并存储。
  5. 页面卸载前上报点击列表;

三、实现

  1. 单独实现坐标换算的方法和点击处理的方法
// 构造器初始化需要的变量
constructor(options = {}){
	// 点击事件列表
	this.clickEventList = [];
	// 坐标换算标准尺寸
	this.coord_convert_standard = {
		pc: {
			portrait: { width: 1920, height: 1080 },
			landscape: { width: 1920, height: 1080 }
        },
        tablet: {
        	portrait: { width: 768, height: 1024 }, // 竖屏
        	landscape: { width: 1024, height: 768 } // 横屏
        },
        mobile: {
        	portrait: { width: 375, height: 667 },
        	landscape: { width: 667, height: 375 }
        }
	};
	this.pointDownCoord = {};
}
// 获取设备类型
getDeviceType(){
	const ua = navigator.userAgent;
	if(/Mobile|Android|iP(ad|hone)/.test(ua)){
		if(/Tablet|iPad/.test(ua)){
			return 'tablet'
        }else{
        	return 'mobile'
        }]
    }else{
    	return 'pc'
    }
}

// 获取屏幕标准及方向
getScreenStandard = () => {
	const device_type = this.getDeviceType();
	if (window.matchMedia("(orientation: portrait)").matches) {
		return {
			direction: 'portrait',
			standard: this.coord_convert_standard[device_type]['portrait']
        }
    } else if (window.matchMedia("(orientation: landscape)").matches) {
    	return {
    		direction: 'landscape',
    		standard: this.coord_convert_standard[device_type]['landscape']
        }
    } else {
    	return undefined;
    }
};

// 坐标换算的方法
coordConvert(standard, coord) {
	if(!standard){
		return {
			direction: 'unknown',
			device_type: this.getDeviceType(),
          	coord
        }
    }
    const scaleX = window.innerWidth / standard.width;
    const scaleY = window.innerHeight / standard.height;
    return {
    	direction: this.getScreenStandard().direction,
        device_type: this.getDeviceType(),
        coord: [ Math.ceil(coord.x * scaleX), Math.ceil(coord.y * scaleY) ]
    }
}
// 点击处理的方法
clickHandler(event, type, coord){
	const element = event.target
	const clickInfo = {
		click_type: type || event.type,
		...this.coordConvert(this.getScreenStandard()?.standard ,{ x: coord?.x || event.pageX, y: coord?.y || event.pageY })
	}
    this.clickEventList.push(clickInfo);
    // 清空pointdown存储的坐标,以免影响页面跳转前的坐标的准确性
    this.pointDownCoord = {};
}
  1. 修改sdk加载阶段(_sdk_init_loading)添加点击事件监听器的代码:
// 添加点击事件监听器(放在_sdk_init_loading中)
let _self = this;
const eventList = ['click', 'dblclick', 'pointerdown'];
let clickTimeout = null;
let isDoubleClick = false;
eventList.forEach((event) => {
	window.addEventListener(event, (e) => {
	 	switch(event){
			case 'click':
       			// 设置延迟来区分单击和双击事件
				clickTimeout = setTimeout(() => {
					if (!isDoubleClick) {
						_self.clickHandler(e);
					}
		         	isDoubleClick = false;
		         }, 300);
		         return;
		    case 'dblclick':
       			// 设置双击标志并取消单击事件(pc端生效)
       			isDoubleClick = true;
       			clearTimeout(clickTimeout);
       			_self.clickHandler(e);
       			return;
     		case 'pointerdown':
       		// 该监听器主要记录那些会被阻止的点击事件的坐标(页面跳转)
       		// mousedown事件可能会被swiper等滑动元素阻止,故使用pointdown事件
       		_self.pointDownCoord = { x: e.pageX, y: e.pageY };
       		return;
   		}
 	});
});
  1. 页面卸载阶段的改造
_sdk_page_beforeunload(){
	let _self = this;
    window.addEventListener('beforeunload', function(){
    	_self.clickHandler(e, undefined, _self.pointDownCoord)
		const params = {
		    click_info_list: _self.clickEventList, // 点击事件列表
		}
	    _self._send_beacon_post('page_unload', params);
    })
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SicaWang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值