UNPKG

@yqg/multiple-click

Version:

Monitor user's multiple click behavior and report

103 lines (86 loc) 3.68 kB
import {MouseEventHandler, MultipleClickTrackingConfig, TriggerItem, TriggerQueue} from './type'; /** 监听器工厂函数 */ export const trackingHandlerFactory = ({ interval, continuousCount, range, uploadTrackingInfo, filter, excludeRules, }: MultipleClickTrackingConfig): MouseEventHandler => { // 缓存click事件的队列 const triggerQueue: TriggerQueue = []; const appendTriggerItem = (item: TriggerItem) => { triggerQueue.push(item); let delIndex = -1; for (let i = 0, len = triggerQueue.length - 1; i < len; i++) { const {timeStamp: t1} = triggerQueue[i]; const {timeStamp: t2} = triggerQueue[i + 1]; if (t2 - t1 > interval) { delIndex = i; } } triggerQueue.splice(0, delIndex + 1); // 简易地判断多个点是否落在一个半径为x的圆内:任意两点的距离小于直径2x for (let i = 0, len = triggerQueue.length; i < len; i++) { // 每次循环前检查,如果当前队列长度小于连续点击次数,直接返回,不进行无效计算 if (triggerQueue.length < continuousCount) { return; } for (let j = i + 1; j < triggerQueue.length; j++) { const {pageX: x1, pageY: y1} = triggerQueue[i]; const {pageX: x2, pageY: y2} = triggerQueue[j]; // 两点距离小于等于直径 if (Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) <= range * 2) { continue; } // 两点距离大于直径,且其中一点为的下标为i,跳出本次循环 const delItem = triggerQueue.shift(); if (delItem === triggerQueue[i]) { break; } } } }; // 监听器 const trackingHandler: MouseEventHandler = (event: MouseEvent) => { try { const {target, timeStamp, pageX, pageY} = event; if (!target || !(target instanceof Element) || target === document.body || filter(event)) { return; } if (excludeRules.length) { const pathname = new URL(location.href).pathname; const isMatch = !!excludeRules.find(({selector, shallow, page}) => { if (page.length && page.indexOf(pathname) === -1) { return false; } return shallow ? target.matches(selector) : target.closest(selector); }); if (isMatch) { return; } } const triggerItem: TriggerItem = { timeStamp, target, pageX, pageY, }; appendTriggerItem(triggerItem); if (triggerQueue.length >= continuousCount) { // 在上报前检查,是否是由于多次点击快捷选中文本而导致的上报 const elementText = (target as HTMLDivElement)?.innerText || (target as HTMLInputElement)?.value; const isSelectionReport = elementText && window.getSelection().toString().includes(elementText); if (!isSelectionReport) { uploadTrackingInfo(event); } triggerQueue.length = 0; } } catch (error) { // eslint-disable-next-line no-console console.error('多次点击上报监听器异常:', error); } }; return trackingHandler; };