JS 监听用户页面访问&页面关闭操作并进行数据上报

前言

最近在做安全方面的项目,有个需求是在用户访问页面和关闭页面的时候,发送对应的数据。

刚拿到需求的时候,觉得没啥东西,init 的时候发送一次,页面 unload 的时候发送一次就行了,很简单,后面开发了一下,又根据当前项目,发现没这么简单

一、需求背景

1、项目需求

用户在页面访问时发送数据到后台,页面关闭时也发送数据到后台。

2、需求解析

很简单的一句话

但是我们前面说了,没有这么简单,那是因为我们的项目比较复杂

  1. 项目是一个庞大的项目,内部有好多子系统,子系统是通过 iframe 内嵌的

  2. 点击 nav 模块进入到子系统,当前页面的 hash 不会改变,只会改变 location.pathname document.title,但是这两个改变有没有事件监听到

  3. 点击进入子系统时,也需要对之前的模块进行关闭上报和当前模块的访问上报

  4. iframe 内嵌的项目不需要单独上报,在 top 层进行上报即可

  5. iframe 内嵌的项目单独通过 URL 访问,则和当前项目一样,需要访问上报和关闭上报

3、需求概括

经过分析,整体需求分为如下几个点:

  1. 第一次进入页面时触发页面访问

  2. 刷新当前页面时触发页面访问

  3. 新 tab 进入页面时触发页面访问

  4. 当前页面点击 nav 进入其他模块时,触发页面关闭&页面访问

  5. 关闭页面时触发页面关闭

二、技术要点

主要包含以下几点:

  1. cookie 存储

  2. sessionStorage 存储

  3. addEventListener 事件监听

  4. navigator.sendBeacon 数据发送

1、Cookie 存储

使用 cookie 主要是因为项目的 domain 都一样,存储不同页面的 titlehrefreferrer 等数据

【Cookie】

2、SessionStorage 存储

这个主要是对在当前 tab 页下的跳转进行判断,用来区分是否首次进入当前 tab

【SessionStorage】

3、addEventListener 事件监听

事件监听,注意是用来监听 unload 事件。

【addEventListener MDN】

4、navigator.sendBeacon 数据发送

这个我们后面在水一篇文章,单独讲讲,本期只讲用法

navigator.sendBeacon() 方法可用于通过 HTTP POST 将少量数据异步传输到 Web 服务器。

4.1. 语法

navigator.sendBeacon(url);
navigator.sendBeacon(url, data);

4.2. 参数

4.2.1. url

url 参数表明 data 将要被发送到的网络地址。

4.2.2. data 可选

data 参数是将要发送的 ArrayBufferArrayBufferViewBlobDOMStringFormDataURLSearchParams 类型的数据。

4.3. 返回值

当用户代理成功把数据加入传输队列时,sendBeacon() 方法将会返回 true,否则返回 false

【navigator.sendBeacon】

三、需求实现

1、实现思路

1.1. 第一次进入页面

  1. sessioncookie

  2. 调用 urlDetectChange 函数

  3. 触发 openPage() 进行页面访问上报

  4. 设置 session 字段

  5. setTimeout 轮询,设置 cookie 字段,监听 URL 变化

1.2. 刷新当前页面

  1. sessioncookie

  2. 触发页面访问上报

  3. setTimeout 轮询,监听 URL 变化

1.3. 点击 nav 进入其他模块

  1. sessioncookie

  2. setTimeout 轮询,当前 document 中的 titlehrefcookie 中的不一致时,进行之前页面关闭上报和当前页面访问上报

  3. 设置新的 cookie

  4. 继续 setTimeout 轮询,监听 URL 变化

1.4. 新 tab 进入页面

  1. cookiesession

  2. 触发页面访问上报

  3. 设置新的 cookie

  4. 继续 setTimeout 轮询,监听 URL 变化

1.5. 关闭 tab

  1. 触发 unload 监听事件
  2. 进行页面关闭上报

2、页面访问->页面关闭流程图

3、页面访问&页面关闭数据上报流程图

4、数据上报

/**
* 上报接口
* @param {object} data 上报接口参数
* @returns {boolean} sendBeacon 接口返回信息
*/
export const sendBeaconMessage = (data: object): boolean =>
window.navigator.sendBeacon(
'xxx',
JSON.stringify(data)
)

5、设置 cookie 的值

// Cookies 使用 js-cookie
/**
* 设置 cookie 值
* 设置 href、pageTitle、referrer 字段
* key 为 ACCESS_CLOSE_COOKIE_NAME
* domain 为 '.xxx.com'
*/
export const setAccessPageCookie = () =>
Cookies.set(
'ACCESS_CLOSE_COOKIE_NAME',
JSON.stringify({
href: location.href,
pageTitle: document.title,
referrer: document.referrer
}),
{ domain: '.xxx.com', expires: 30 }
)

6、页面访问发送消息

/**
* 页面访问发送消息
* @param {object} data 上报接口参数
* 设置 sessionStorage 的值,
*/
export const openPageSendBeacon = async (data: object) => {
sessionStorage.setItem('ACCESS_CLOSE_SESSION_NAME', 'ISTRUE')
const sendBeaconSusscess = sendBeaconMessage(data)
// 打印 sendBeaconSusscess 的值
console.log(
'%c client:sendDataToRemote use sendBeacon access page: %o',
'color: green;',
sendBeaconSusscess
)
}

7、页面关闭发送消息

/**
* 页面关闭发送消息
* @param {object} data 上报接口参数
*/
export const closePageSendBeacon = async (data: object) => {
const sendBeaconSusscess = sendBeaconMessage(data)
// 打印 sendBeaconSusscess 的值
console.log(
'%c client:sendDataToRemote use sendBeacon close page: %o, ',
'color: red;',
sendBeaconSusscess
)
}

8、URL 改变进行监听&页面关闭监听

/**
* URL 改变事件
*/
export const urlDetectChange = () => {
const accessPageData = Cookies.get('ACCESS_CLOSE_COOKIE_NAME')
? JSON.parse(Cookies.get('ACCESS_CLOSE_COOKIE_NAME'))
: null
const sessionAccessData = sessionStorage.getItem('ACCESS_CLOSE_SESSION_NAME')
// 第一次进入页面 和 新 tab 进入页面
if (!accessPageData || !sessionAccessData) {
openPageSendBeacon({})
}
setTimeout(() => {
if (
accessPageData &&
location.href !== accessPageData.href &&
document.title !== accessPageData.pageTitle &&
sessionAccessData
) {
// 点击 nav 进入其他模块
closePageSendBeacon({})
openPageSendBeacon({})
}
setAccessPageCookie()
urlDetectChange()
}, 1000)
}
// 加这个是针对 iframe 内部的项目不进行监听,只在 top 层进行数据上报
if (window.top === window.self) {
// 页面访问上报 刷新页面
sessionStorage.getItem('ACCESS_CLOSE_SESSION_NAME') && openPageSendBeacon({})
urlDetectChange()
window.addEventListener('unload', () => closePageSendBeacon({}))
}

四、总结

  1. 页面访问&页面关闭数据上报能清楚的掌握用户的使用数据,对营销活动或者画像分析很有帮助
  2. 整体没有难点,就是不同项目不同分析
  3. 如果你的项目是 hash 改变,那就可以针对 hash 进行监听
  4. 主要就是使用 navigator.sendBeacon 进行可靠的数据传输

JS 监听用户页面访问&页面关闭并进行数据上报操作的更多相关文章

  1. js中对arry数组的各种操作小结 瀑布流AJAX无刷新加载数据列表--当页面滚动到Id时再继续加载数据 web前端url传递值 js加密解密 HTML中让表单input等文本框为只读不可编辑的方法 js监听用户的键盘敲击事件,兼容各大主流浏览器 HTML特殊字符

    js中对arry数组的各种操作小结   最近工作比较轻松,于是就花时间从头到尾的对js进行了详细的学习和复习,在看书的过程中,发现自己平时在做项目的过程中有很多地方想得不过全面,写的不够合理,所以说啊 ...

  2. js监听用户的键盘敲击事件,兼容各大主流浏览器

    js监听用户的键盘敲击事件,兼容各大主流浏览器 <script type="text/javascript"> document.onkeydown = functio ...

  3. 【js监听报错】页面监听js报错问题

    <html> <head> <script type="text/javascript"> // 页面监听js报错问题 onerror=hand ...

  4. js监听用户思否在当前页面

    (function () { var t var hiddenProperty = 'hidden' in document ? 'hidden' : 'webkitHidden' in docume ...

  5. js监听离开或刷新页面时的弹窗提示

    一.看图 二.使用场景. 填写表单时内容,当离开页面或者刷新的时候回丢失页面的内容,因此人性化的设计该有一个提示.所以这样的功能也就应用而生了. 三.思路. 1,页面内容改变.2,离开或刷新浏览器触发 ...

  6. js 监听整个页面的回车事件

    JS监听整个页面的回车事件 <script type="text/javascript"> document.onkeydown=keyDownSearch;      ...

  7. js监听页面放大缩小

    <!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>d ...

  8. vue.js 作一个用户表添加页面----初级

    使用vue.js 制作一个用户表添加页面,实际上是把原来需要使用js写的部分,改写成vue.js的格式 首先,想象一下,先做思考,我们要添加用户表,设涉及到哪些数据,一个是用户id,一个是用户名,一个 ...

  9. js监听输入框值的即时变化onpropertychange、oninput

    js监听输入框值的即时变化onpropertychange.oninput 很多情况下我们都会即时监听输入框值的变化,以便作出即时动作去引导浏览者增强网站的用户体验感. // //   要达到的效果 ...

  10. JS监听组合按键

    有些时候,我们需要在网页上,增加一些快捷按键,方便用户使用一些常用的操作,比如:保存,撤销,复制.粘贴等等. 下面简单梳理一下思路: 我们所熟悉的按键有这么集中类型: 单独的按键操作,如:delete ...

随机推荐

  1. 父组件明明使用了v-model,子组件竟然可以不用定义props和emit抛出事件,快来看看吧

    前言 vue3.4增加了defineModel宏函数,在子组件内修改了defineModel的返回值,父组件上v-model绑定的变量就会被更新.大家都知道v-model是:modelValue和@u ...

  2. ssm 创建bean的三种方式和spring依赖注入的三种方式

    <!--创建bean的第一种方式:使用默认无参构造函数 在默认情况下: 它会根据默认无参构造函数来创建类对象.如果 bean 中没有默认无参构造函数,将会创建失败--> <bean ...

  3. redis 简单整理——客户端常见异常[十七]

    前言 这个还是比较常见的,也就是比较对开发有用的部分. 正文 1.无法从连接池获取到连接 JedisPool中的Jedis对象个数是有限的,默认是8个.这里假设使用的默 认配置,如果有8个Jedis对 ...

  4. redis 简单整理——redis 的有序集合基本结构和命令[六]

    前言 简单介绍一下redis的有序集合的基本结构和命令. 正文 有序集合相对于哈希.列表.集合来说会有一点点陌生,但既然叫有序 集合,那么它和集合必然有着联系,它保留了集合不能有重复成员的特性, 但不 ...

  5. c# TryParse

    c# tryparse和parse对比,大体内容是一致的. parse 会返回异常,主要表现为三种. 第一种ArgumentNullException,这里面表示,为空. 第二种formatexcep ...

  6. 《c#高级编程》第3章C#3.0中的更改(五)——扩展方法

    C#扩展方法是一种语法,可以为已有的类添加新的实例方法,而无需修改原来的类定义.它的语法形式为: ```csharppublic static void MyExtensionMethod(this ...

  7. 【笔记】Oracle列转行unpivot&行转列 PIVOT

    unpivot 说明:将表中多个列缩减为一个聚合列(多列转多行) 语法:unpivot(新列名 for 聚合列名 in (对应的列名1-列名n )) 写到了一个力扣的题,发现这个unpivot函数还没 ...

  8. 聊聊日志硬扫描,阿里 Log Scan 的设计与实践

    简介: SLS 新推出 Scan 功能,让未索引的字段也支持搜索(硬扫描模式),节省全量索引产生的构建和存储费用,同时 Scan 的运行时计算模式对于杂乱结构的日志数据有更好的适配,帮助企业客户实现数 ...

  9. 从KPI到OKR,高阶产品人如何推动业务高速增长

    简介: 不管是核心大目标,还是O(Objectives),或者北极星指标,奇妙等式等等,最后都需要核心组织协同方式来推动整个目标聚焦以及过程的落地. 作为产品经理人,相信很多人都遇到过以下的灵魂拷问: ...

  10. EventBridge 事件总线及 EDA 架构解析

    ​简介:EventBridge 是事件驱动的具体落地产品,也是 EDA 的最佳实践方式. 作者:肯梦 作为 Gartner 定义的 10 大战略技术趋势之一,事件驱动架构(EDA)逐渐成为主流技术架构 ...