UNPKG

x-track

Version:
296 lines (256 loc) 7.83 kB
/* * @description 页面埋点统计(停留时间) * * 当浏览器进入页面时,触发onload * 当关闭浏览器时,执行顺序 onbeforeunload--> visibilitychange --> onunload * 每一次进入页面 entry_time 记当前时间戳,初始化时记一次当前时间戳 * 每一次隐藏页面 hidden_time 记当前时间戳, onbeforeunload时再记一次当前时间戳 * hidden_time[i] - entry_time[i] 位每次停留的时间,求和为总的停留时间。 * * * 关闭窗口请求参考链接:https://usefulangle.com/post/62/javascript-send-data-to-server-on-page-exit-reload-redirect * * 1. 可用ajax 同步请求,但是对用户不友好 * 2. 可用ajax 异步请求,后端可能需要配置 ignore_user_abort * 3. 可用 navigator.sendBeacon,不兼容ie。(推荐这个) * * trackCb 接收的数据 * { * split_time, // 页面停留时间 [1400, 1200, 1000] * stay_time, // 页面总的停留时间 3600 * * } * * @example * new PageView({trackCb: fn}) */ function PageView(option) { // 计算页面显示时的时间戳 this.entry_time = [], // 计算页面隐藏时的时间戳 this.hidden_time = [], // 停留时间 总的停留时间 = 停留时间累加 this.split_time = [], // 总的停留时间 this.stay_time = 0, this.trackCb = option && option.trackCb ? option.trackCb : null, // 初始化 this.init(); } // 页面 进入 PageView.prototype.countEntryTime = function () { this.entry_time.push(new Date().getTime()); }; // 页面 隐藏 PageView.prototype.countHiddenTime = function () { this.hidden_time.push(new Date().getTime()); }; // 组装数据 PageView.prototype.getAnalysisData = function () { let aEntryTime = this.entry_time, aHiddenTime = this.hidden_time; // 计算停留时间 for (let i = 0; i < aHiddenTime.length; i++) { let t = +((aHiddenTime[i] - aEntryTime[i])).toFixed() this.split_time.push(t); } // 计算停留总时长 let nStayTime = 0; for (let i = 0; i < this.split_time.length; i++) { nStayTime += this.split_time[i]; }; nStayTime = +nStayTime.toFixed(); this.stay_time = nStayTime; return { split_time: this.split_time, stay_time: this.stay_time, page: this.page } }; // 关闭或刷新页面数据处理 回调页面逻辑 PageView.prototype.setAnalysis = function () { let data = this.getAnalysisData(); if (data.stay_time) { this.trackCb && this.trackCb(data); } else { console.error('停留时间计算错误'); } }; // 关闭或刷新页面 PageView.prototype.initCloseWindow = function () { let self = this; // onbeforeunload onunload https://stackoverflow.com/questions/6895564/difference-between-onbeforeunload-and-onunload this.addEventListener(window, 'beforeunload', function (event) { // 关闭或刷新时 记一次hidden时间 self.countHiddenTime(); self.setAnalysis(); }, false); }; /** * @description 页面 显示隐藏 * 参考: https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API */ PageView.prototype.initChangeVisible = function () { let self = this; function handleVisibilityChange() { // 页面隐藏计算时间 if (document[hidden]) { self.countHiddenTime(); } else { // 页面显示统计时间 self.countEntryTime(); } } // 设置隐藏属性和改变可见属性的事件的名称 var hidden, visibilityChange; if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support hidden = "hidden"; visibilityChange = "visibilitychange"; } else if (typeof document.msHidden !== "undefined") { hidden = "msHidden"; visibilityChange = "msvisibilitychange"; } else if (typeof document.webkitHidden !== "undefined") { hidden = "webkitHidden"; visibilityChange = "webkitvisibilitychange"; } // 兼容 addEventListener this.addEventListener(document, visibilityChange, handleVisibilityChange, false); }; // 监听事件 兼容性 PageView.prototype.addEventListener = function (ele, event, fn, bubble) { if (ele.addEventListener) { ele.addEventListener(event, fn, bubble); } else { ele.attachEvent('on' + event, fn); } }; PageView.prototype.init = function () { // 第一次进入页面时 记entry时间 this.countEntryTime(); // 初始化 页面隐藏时间 this.initChangeVisible(); // 初始化 页面关闭或刷新事件 this.initCloseWindow(); }; var pageView = new PageView({trackCb: null}); /** * @description 页面埋点 * * example: * var st = stat(option1); * st(option2); * * option1传项目(和页面)公共参数 * option2传当前触发参数 * * option1 和 option2 结构相同、option2可覆盖option1 * * { * base: {}, * yc_log: {}, * lg_vl: {} * } */ function stat(option) { // 容错机制 if (!option) option = {}; // 私有参数 var lg_vl = { url: window.location.href, ref: document.referrer }; if (option.lg_vl) oBjectAssign(lg_vl, option.lg_vl); // 共有参数 var yc_log = { agt: navigator.userAgent, // 浏览器信息 lg_vl: lg_vl }; if (option.yc_log) oBjectAssign(yc_log, option.yc_log); // 基本参数 var oData = { ltype: '', // R 访问类型 yc_log: yc_log }; if (option.base) oBjectAssign(oData, option.base); return function (option2) { var data = JSON.parse(JSON.stringify(oData)); if (option2.base) oBjectAssign(data, option2.base); if (option2.yc_log) oBjectAssign(data.yc_log, option2.yc_log); if (option2.lg_vl) oBjectAssign(data.yc_log.lg_vl, option2.lg_vl); data.yc_log.itime = new Date().getTime(); // 日志发生时间 // console.log('data', JSON.parse(JSON.stringify(data))); // tpye 停留时长 if (data.ltype === 'tlsc') { pageView.trackCb = function(oTime) { data.yc_log.itime = new Date().getTime(); data.yc_log.lg_vl.tlsc_dur = oTime.stay_time; // 停留时长时间字段 httpAjaxGet('http://www.baidu.com', data); } } else { // tpye 浏览、点击 httpAjaxGet('http://www.baidu.com', data); } } } /** * @description 合并对象到 arg[0] * * @param {Object} obj1 * @param {Object} obj2 * */ function oBjectAssign(obj1, obj2) { for (var key in obj2) { obj1[key] = obj2[key]; } }; /** * @description 封装ajax get, 兼容ie8 * * @param {String} url 请求url * @param {Object} data 请求参数 * @param {Function} cb 成功回调 * */ function httpAjaxGet(url, data, cb) { var xmlhttp = null; if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); } else if (window.ActiveXObject) { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }; // get params string var data2str = parseParams(data); if (xmlhttp != null) { xmlhttp.open("GET", url + '?' + data2str, false); //第一个参数指明访问方式,第二次参数是目标url,第三个参数是“是否异步”,true表示异步,false表示同步 xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { var d = xmlhttp.responseText; // 处理返回结果 cb && cb(); } } xmlhttp.send(); } } /** * @description 设置url参数 data2str params * * @param {Object} data 参数 * @returns {String} 返回 qarams string */ function parseParams(data) { var data2str = ''; for (var key in data) { var value = data[key]; if (Object.prototype.toString.call(data[key]) === '[object Object]') { value = encodeURIComponent(JSON.stringify(value)); } data2str += key + '=' + value + '&'; } data2str = data2str && data2str.slice(0, -1); return data2str; }; export { stat }