簡(jiǎn)單實(shí)現(xiàn)前端無痕埋點(diǎn)

什么是無痕埋點(diǎn)(smart-tracker)

傳統(tǒng)的埋點(diǎn)形式备埃,都是手動(dòng)埋點(diǎn),在指定的元素上綁定事件,將用戶行為信息發(fā)送到服務(wù)端進(jìn)行統(tǒng)計(jì)鸭限。但是當(dāng)引入無痕埋點(diǎn)的庫以后,用戶在瀏覽器里所有行為和操作都會(huì)被自動(dòng)記錄下來两踏,并將信息發(fā)送到后端進(jìn)行統(tǒng)計(jì)和分析败京。

我們?yōu)槭裁匆鰺o痕埋點(diǎn)

提高工作效率,解放雙手

無痕埋點(diǎn)原理

這里只講click的無痕埋點(diǎn)原理梦染。

當(dāng)用戶點(diǎn)擊了頁面上某一個(gè)元素赡麦,我們要把當(dāng)前元素到body之間整個(gè)dom的路徑記錄下來,作為這個(gè)元素的唯一標(biāo)識(shí)帕识,我們稱之為domPath泛粹,這個(gè)domPath不僅是這個(gè)元素唯一標(biāo)識(shí),還可以通過document.querySelector(domPath)去唯一選擇和定位到這個(gè)元素肮疗,當(dāng)用戶點(diǎn)擊一次這個(gè)元素戚扳,就會(huì)將埋點(diǎn)數(shù)據(jù)上傳到服務(wù)器,服務(wù)器上這個(gè)domPath對(duì)應(yīng)的統(tǒng)計(jì)數(shù)據(jù)加一族吻。

無痕埋點(diǎn)代碼實(shí)現(xiàn)

document.body.addEventListener('click',  (event) => {
    const eventFix = getEvent(event);
    if (!eventFix) {
        return;
    }
    this._handleEvent(eventFix);
}, false)

首先在document的body上監(jiān)聽和綁定全局click事件帽借,捕獲用戶所有的點(diǎn)擊事件。

// 關(guān)鍵代碼
const getDomPath = (element, useClass = false) => {
    if (!(element instanceof HTMLElement)) {
        console.warn('input is not a HTML element!');
        return '';
    }
    let domPath = [];
    let elem = element;
    while (elem) {
        let domDesc = getDomDesc(elem, useClass);
        if (!domDesc) {
            break;
        }
        domPath.unshift(domDesc);
        if (querySelector(domPath.join('>')) === element || domDesc.indexOf('body') >= 0) {
            break;
        }
        domPath.shift();
        const children = elem.parentNode.children;
        if (children.length > 1) {
            for (let i = 0; i < children.length; i++) {
                if (children[i] === elem) {
                    domDesc += `:nth-child(${i + 1})`;
                    break;
                }
            }
        }
        domPath.unshift(domDesc);
        if (querySelector(domPath.join('>')) === element) {
            break;
        }
        elem = elem.parentNode;
    }
    return domPath.join('>');
}

獲取元素唯一標(biāo)識(shí)domPath這段代碼是關(guān)鍵超歌。getDomPath函數(shù)傳入的是用戶點(diǎn)擊事件的target對(duì)象: getDomPath(event.target)砍艾。

主要思路是找到當(dāng)前元素event.target,然后不斷的去循環(huán)找它的父節(jié)點(diǎn)parentNode巍举,將父節(jié)點(diǎn)的tagName當(dāng)做domPath路徑上的節(jié)點(diǎn)脆荷,如果當(dāng)前元素有id,那就取消所有路徑的循環(huán),直接講id賦值給domPath蜓谋。

const children = elem.parentNode.children;
if (children.length > 1) {
    for (let i = 0; i < children.length; i++) {
        if (children[i] === elem) {
            domDesc += `:nth-child(${i + 1})`;
            break;
        }
    }
}
domPath.unshift(domDesc);

getDomPath函數(shù)中的這段代碼意思是在同一級(jí)上出現(xiàn)了多個(gè)相同tagName元素梦皮,那我們要定位到這個(gè)event.target這個(gè)元素在這一級(jí)里的第幾個(gè),假設(shè)這個(gè)div是同一級(jí)的第三個(gè)桃焕,那返回的就是div:nth-child(3)剑肯,這樣就可以在document.querySelector(domPath)里唯一定位到這個(gè)元素。

_handleEvent(event) {
    const domPath = getDomPath(event.target);
    const rect = getBoundingClientRect(event.target);
    if (rect.width == 0 || rect.height == 0) {
        return;
    }
    let t = document.documentElement || document.body.parentNode;
    const scrollX = (t && typeof t.scrollLeft == 'number' ? t : document.body).scrollLeft;
    const scrollY = (t && typeof t.scrollTop == 'number' ? t : document.body).scrollTop;
    const pageX = event.pageX || event.clientX + scrollX;
    const pageY = event.pageY || event.clientY + scrollY;
    const data = {
        domPath: encodeURIComponent(domPath),
        trackingType: event.type,
        offsetX: ((pageX - rect.left - scrollX) / rect.width).toFixed(6),
        offsetY: ((pageY - rect.top - scrollY) / rect.height).toFixed(6),
    };
    this.send(data);
}

這段代碼就是得到用戶點(diǎn)擊某個(gè)元素的相對(duì)位置的橫向位置和豎向位置比例观堂,得到這個(gè)位置的值让网,就可以反向從埋點(diǎn)數(shù)據(jù)中得到用戶點(diǎn)擊元素的具體位置,因?yàn)槭莻€(gè)比例值师痕,所以在反向推導(dǎo)中還能自適應(yīng)頁面大小的改變溃睹。

send(data = {}) {
    const image = new Image(1, 1);
    image.onload = function () {
        image = null;
    };
    image.src = `/?${stringify(data)}`;
}

得到了用戶點(diǎn)擊的位置信息和唯一標(biāo)識(shí)domPath,就可以將數(shù)據(jù)發(fā)送到服務(wù)端進(jìn)行統(tǒng)計(jì)了
用image的src胰坟,將數(shù)據(jù)進(jìn)行傳輸因篇。
用image的src有個(gè)好處就是輕量,并且還支持跨域笔横,打點(diǎn)基本上都用的這個(gè)方法進(jìn)行發(fā)送數(shù)據(jù)竞滓。

結(jié)尾

這里講的僅僅只是無痕埋點(diǎn)的一個(gè)簡(jiǎn)單實(shí)現(xiàn),對(duì)整個(gè)無痕埋點(diǎn)體系來說狠裹,這些只是冰山一角虽界。

真正的無痕埋點(diǎn),還需要做統(tǒng)計(jì)涛菠、分析莉御、差量預(yù)測(cè)、標(biāo)記策略俗冻、智能降噪礁叔、可視化無痕、無痕分桶迄薄、反向推導(dǎo)熱力圖琅关、大數(shù)據(jù)中臺(tái)等等,涉及到前端讥蔽、后端涣易、運(yùn)維、DBA和算法冶伞。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末新症,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子响禽,更是在濱河造成了極大的恐慌徒爹,老刑警劉巖荚醒,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異隆嗅,居然都是意外死亡界阁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門胖喳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泡躯,“玉大人,你說我怎么就攤上這事禀晓【” “怎么了坝锰?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵粹懒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我顷级,道長(zhǎng)凫乖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任弓颈,我火速辦了婚禮帽芽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘翔冀。我一直安慰自己导街,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布纤子。 她就那樣靜靜地躺著搬瑰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪控硼。 梳的紋絲不亂的頭發(fā)上泽论,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音卡乾,去河邊找鬼翼悴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛幔妨,可吹牛的內(nèi)容都是我干的鹦赎。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼误堡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼古话!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起埂伦,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤煞额,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體膊毁,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胀莹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了婚温。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片描焰。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖栅螟,靈堂內(nèi)的尸體忽然破棺而出荆秦,到底是詐尸還是另有隱情,我是刑警寧澤力图,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布步绸,位于F島的核電站,受9級(jí)特大地震影響吃媒,放射性物質(zhì)發(fā)生泄漏瓤介。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一赘那、第九天 我趴在偏房一處隱蔽的房頂上張望刑桑。 院中可真熱鬧,春花似錦募舟、人聲如沸祠斧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽琢锋。三九已至,卻和暖如春觅彰,著一層夾襖步出監(jiān)牢的瞬間吩蔑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工填抬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烛芬,地道東北人飒责。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓赘娄,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親宏蛉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子遣臼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容