前言
大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心~
背景
如果在做某個平臺的時候,我們需要統(tǒng)計用戶點擊的次數(shù),點擊的區(qū)域,點擊元素,等等,那我們應該怎么去做比較合適呢?
舉個例子,我想在用戶點擊頁面上的每一個元素時,我都能把這個元素的DOM節(jié)點信息記錄下來,并且上報到服務器,便于后面產品那邊的統(tǒng)計用戶喜好~
公共函數(shù)?處處調用?
那我們要怎么去做呢?寫一個公共函數(shù)嗎?然后去統(tǒng)一做上報嗎?
我首先寫一個函數(shù),這是一個獲取點擊元素信息的函數(shù),我們可以在點擊的 event 參數(shù)中拿到目標元素 target
圖片
const reportDOM = (e: PointerEvent) => {
// 獲取到點擊的目標元素
const el = e.target
// 把目標元素解析成字符串
const detAIl = htmlElementAsString(el)
// 進行上報
report(detail)
}
// 上報函數(shù)
export const report = (detail) => {
request(url, detail)
}
// 解析函數(shù)
export function htmlElementAsString(target: HTMLElement): string {
const tagName = target.tagName.toLowerCase();
if (tagName === 'body') {
return '';
}
let classNames = target.classList.value;
classNames = classNames !== '' ? ` class='${classNames}'` : '';
const id = target.id ? ` id="${target.id}"` : '';
const innerText = target.innerText;
return `<${tagName}${id}${classNames !== '' ? classNames : ''}>${innerText}</${tagName}>`;
}
寫完這幾個函數(shù)之后,我們只需要在每一個點擊事件中去插入這個函數(shù)即可
const click1 = (e: PointerEvent) => {
reportDOM(e)
// coding....
}
const click2 = (e: PointerEvent) => {
reportDOM(e)
// coding....
}
const click3 = (e: PointerEvent) => {
reportDOM(e)
// coding....
}
但是一個頁面中,點擊事件非常多啊,不可能每一個事件中去插入這個函數(shù),非常麻煩
全局監(jiān)聽 + elementFromPoint
基本做法
最好的辦法就是把 click 事件掛載在 window 身上,然后根據(jù) elementFromPoint 去計算坐標匹配的元素,進行解析上報
window.addEventListener(
'click',
(e: PointerEvent) => {
// 通過坐標計算出目標元素
const el = getTargetDomByPointerEvent(e);
if (!el) return;
// 把目標元素解析成字符串
const detail = htmlElementAsString(el);
// 進行上報
report(detail);
},
true,
);
// 通過坐標計算目標元素
export const getTargetDomByPointerEvent = (e: PointerEvent) => {
const el = document.elementFromPoint(e.pageX, e.pageY);
if (el) {
return el as HTMLElement;
}
return null;
};
拓展做法,只上報所需元素
我們可以通過配置一個數(shù)組 globalClickListeners ,只對我們所需要的 DOM 節(jié)點進行監(jiān)聽上報,
const globalClickListeners = [
{
selector: '.cla', // 選擇器
},
{
elementText: 'report2', // 元素文本
},
{
selector: '.r', // 選擇器 + 元素文本
elementText: 'report3',
},
];
那么我們需要對 window 的點擊監(jiān)聽進行改造
window.addEventListener(
'click',
(e: PointerEvent) => {
const el = getTargetDomByPointerEvent(e);
if (!el) return;
if (globalClickListeners.length) {
globalClickListeners.forEach(({ selector, elementText, data = '' }) => {
if (selector) {
// 選擇器的情況
const els = document.querySelectorAll(selector);
// 點擊元素是否包含所屬選擇器范圍
const isIncludes = [...(els as unknown as any[])].includes(el);
// 包含則上報
if (isIncludes) {
const detail = htmlElementAsString(el);
// 進行上報
report(detail);
}
} else if (el.textContent === elementText) {
// 文本相同情況
const detail = htmlElementAsString(el);
// 進行上報
report(detail);
}
});
}
},
true,
);
小結
其實上面就是埋點庫中,全局點擊上報的一種解決方案,看似小問題,但是其實面試了這么多人,感覺只有很少一部分人能回答的比較好~
結語
我是林三心
- 一個待過小型toG型外包公司、大型外包公司、小公司、潛力型創(chuàng)業(yè)公司、大公司的作死型前端選手;
- 一個偏前端的全干工程師;
- 一個不正經的掘金作者;
- 逗比的B站up主;
- 不帥的小紅書博主;
- 喜歡打鐵的籃球菜鳥;
- 喜歡歷史的乏味少年;
- 喜歡rap的五音不全弱雞