简单实现前端无痕埋点

什么是无痕埋点(smart-tracker)

传统的埋点形式,都是手动埋点,在指定的元素上绑定事件,将用户行为信息发送到服务端进行统计。但是当引入无痕埋点的库以后,用户在浏览器里所有行为和操作都会被自动记录下来,并将信息发送到后端进行统计和分析。

我们为什么要做无痕埋点

提高工作效率,解放双手

无痕埋点原理

这里只讲click的无痕埋点原理。

当用户点击了页面上某一个元素,我们要把当前元素到body之间整个dom的路径记录下来,作为这个元素的唯一标识,我们称之为domPath,这个domPath不仅是这个元素唯一标识,还可以通过document.querySelector(domPath)去唯一选择和定位到这个元素,当用户点击一次这个元素,就会将埋点数据上传到服务器,服务器上这个domPath对应的统计数据加一。

无痕埋点代码实现

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

首先在document的body上监听和绑定全局click事件,捕获用户所有的点击事件。

// 关键代码
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('>');
}

获取元素唯一标识domPath这段代码是关键。getDomPath函数传入的是用户点击事件的target对象: getDomPath(event.target)。

主要思路是找到当前元素event.target,然后不断的去循环找它的父节点parentNode,将父节点的tagName当做domPath路径上的节点,如果当前元素有id,那就取消所有路径的循环,直接讲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函数中的这段代码意思是在同一级上出现了多个相同tagName元素,那我们要定位到这个event.target这个元素在这一级里的第几个,假设这个div是同一级的第三个,那返回的就是div:nth-child(3),这样就可以在document.querySelector(domPath)里唯一定位到这个元素。

_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);
}

这段代码就是得到用户点击某个元素的相对位置的横向位置和竖向位置比例,得到这个位置的值,就可以反向从埋点数据中得到用户点击元素的具体位置,因为是个比例值,所以在反向推导中还能自适应页面大小的改变。

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

得到了用户点击的位置信息和唯一标识domPath,就可以将数据发送到服务端进行统计了
用image的src,将数据进行传输。
用image的src有个好处就是轻量,并且还支持跨域,打点基本上都用的这个方法进行发送数据。

结尾

这里讲的仅仅只是无痕埋点的一个简单实现,对整个无痕埋点体系来说,这些只是冰山一角。

真正的无痕埋点,还需要做统计、分析、差量预测、标记策略、智能降噪、可视化无痕、无痕分桶、反向推导热力图、大数据中台等等,涉及到前端、后端、运维、DBA和算法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,881评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,052评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,598评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,407评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,823评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,872评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,037评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,778评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,505评论 1 247
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,745评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,233评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,231评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,141评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,939评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,954评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,784评论 2 275

推荐阅读更多精彩内容