Build a visual user behavior track management system SDK

1, Basic principles

● 1. Using the uniqueness of xpath, bind the management element and add events to send data management
● 2. The background management system establishes a function of visually selecting management elements and saving the configuration
● 3. The front end obtains the dotting configuration according to the page URL for initialization (binding events through xpath)
The basic process is shown in the figure:

2, Method of sending management data from the front end

The front end has several schemes to send dotting data

1. Traditional ajax request

Using traditional ajax request to send data, the disadvantage is that it is easy to block the request and is not friendly to users
Moreover, it has great disadvantages. When users close the page, the request will be truncated, that is, the sending will be terminated. It is not applicable to record the browsing time, data); // Take Axios as an example

2. Dynamic picture

We can delay the unloading by creating a picture element in the beforeunload event handler and setting its src attribute to ensure the sending of data. Because most browsers delay the unloading to ensure the loading of pictures, the data can be sent in the unloading event.

const sendLog = (url, data) => {
  let img = document.createElement('img');
  const params = [];
  Object.keys(data).forEach((key) => {
  img.onload = () => img = null;
  img.src = `${url}?${params.join('&')}`;

3. sendBeacon

In order to solve the above problems, there is navigator Sendbeacon method, which is used to send requests, can ensure the effective delivery of data without blocking the unloading or loading of pages, and the coding is simpler than the above methods.

export const sendBeacon = (url, analyticsData) => {
    const apiUrl = config.apiRoot + url
    let data = getParams(analyticsData)
    // Compatible with browsers that do not support sendBeacon
    if (!navigator.sendBeacon) {
        const client = new XMLHttpRequest()
        // The third parameter indicates synchronous transmission'POST', apiUrl, false)
        client.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
    const formData = new FormData()
    Object.keys(analyticsData).forEach((key) => {
        let value = analyticsData[key]
        if (typeof value !== 'string') {
            // formData can only append string or Blob
            value = JSON.stringify(value)
        formData.append(key, value)
    navigator.sendBeacon(apiUrl, formData)

Finally, we use dynamic pictures because Alibaba cloud provides Alibaba cloud - collect logs through WebTracking Deal with a large number of data collection without causing server pressure on the website itself

3, Build SDK

Use webpack to build a project, package the js file package of a single sdk, and introduce the sdk into the front end (this part will not be repeated, and you can search webpack related materials if you are interested) Write a simple JS SDK with webpack
● visual selection of xpath reference plug-ins
Main functions of SDK:

  1. Expose the initialization method and the dotting method (to support manual dotting)
  2. Add the function of selecting xpath and expose it to the background management system
  3. Read the management configuration list according to the link URL
  4. Initialize the binding event function
  5. Enter the page and record a dot
  6. Record browsing duration
  7. SDK and parent iframe communication function (to transfer data to the background management system)
    Example of recording tour duration:
// Statistical duration
const viewTime = (data) => {
  let startTime = new Date().getTime() // Browse start time
  let endTime = null // Browse end time
  // Page unload trigger
  window.addEventListener('unload', () => {
    endTime = new Date().getTime()
    let params = {
      viewTime: (endTime - startTime) / 1000,
      eventType: 'view',
      accessId: ACCESS_ID
    params = Object.assign(params, data)
  }, false)
// Select xpath to communicate across domains and pages
import Postmate from 'postmate'
import Inspector from '../plugins/inspect' // Select the xpath node plug-in

let childIframe = null
const myInspect = new Inspector()
const getXpathForm = function (options) {
    myInspect.setOptions(options, (data) => {
        let params = {
            xpath: data,
            route: window.QWK_ANALYSIS_SDK_OPTIONS?.route || ''
        childIframe.emit('send-data-event', params)
export default {
    // Communicate with parent iframe
    initMessage () {
        // Enable selected node debugging in development mode
        if (process.env.BUILD_ENV === 'dev') {
            document.querySelector('#selected').onclick = () => {
                    deactivate: true
                }, (data) => {
        const handshake = new Postmate.Model({
            // iframe parent gets xpath
            getXpath: (options) => {
            // Remove selection
            deactivate: () => {
        // When parent <-> child handshake is complete, events may be emitted to the parent
        handshake.then((child) => {
            childIframe = child
// Export SDK
// main.js entry file
import { init } from './lib/init'
import action from './lib/action'
import selectXpath from './lib/select-xpath'
// import { documentReady } from './plugins/common'

// Initialize cross domain communication of selected xpath

// documentReady(() => {
//     //Initialize
//     // init().then(res => res)
// })

// Export SDK module
export {

4, Background management system building visual selection xpath

Step 1: introduce SDK into third-party websites

Write an xpath function in the sdk and expose it to the background management system
● visual selection of xpath reference plug-ins

Step 2: build a management system

Build an iframe to load the website, as shown in the figure:

Here we need to call the method of selecting the website xpath function in the SDK, which must communicate with the website in the loaded iframe
Because it is a third-party website loaded by iframe, there will be cross domain problems, so we need a plug-in to realize this function, post
GitHub link

    <div class="iframe-box" ref="content">

    import Postmate from 'postmate'

    export default {
        name: 'WebIframe',
        props: {
            src: {
                type: String,
                default: ''
            options: {
                type: Object,
                default: () => ({})
        data () {
            return {
                $child: null
        mounted () {
            this.$nextTick(() => this.initMessage())
        methods: {
            initMessage () {
                let handshake = new Postmate({
                    container: this.$refs.content,
                    url: this.src,
                    name: 'web-iframe-name',
                    classListArray: ['iframe-class-content'],
                handshake.then((child) => {
                    this.$child = child
                    child.on('send-data-event', (data) => {
                        this.$emit('selected', data)
            // Get select xpath
            getXpath () {
                let options = {
                    clipboard: false, // Auto copy
                    deactivate: true, // Destroy after selection
                try {
                    this.$'getXpath', options)
                } catch (e) {
                    this.$errMsg('load SDK Failed, please try reloading the website~It is also possible that the current website does not introduce user behavior tracking SDK,Please contact relevant personnel to add')
            // Remove selected layer
            remove () {
                this.$child && this.$'deactivate')

Select the node result, and the effect is shown in the figure below:

5, Problems encountered and Solutions

1. Communication problem of loading third-party website by background management system iframe

Because the third-party website is loaded through iframe for visual management, the parent iframe needs to communicate with the third-party website, but there will be cross domain problems. There are many solutions to cross domain problems. Here, the third-party plug-in post based on postmessage is used to solve them

2. Dynamic routing problem

Encounter pages such as article details, because there will be many links to article details Like this, the following detail/id is followed by many IDs. Such a page cannot be configured in every article. In this way, dynamic routing configuration needs to be done. Unified dynamic parameters are identified with other characters to load the configuration.
① Add a dynamic route ID in the configuration and read the database through the back end to match the dynamic route (the back end needs to do a lot of matching)
② Pure front-end operation. The front-end sdk and the background management system transfer dynamic routing to each other
After discussion, we choose the second one. The dynamic route is sent in when the front end initializes the SDK. At this time, when the dynamic route is received in the SDK, the configuration is loaded according to this route, and the SDK is passed to the background to select the xpath configuration for visualization. The configuration should also be saved through this route
At this time, the two sides can correspond one by one. We define such a route {id}, where {id} is a dynamic parameter

3. The dynamic node cannot be bound to the event problem

Because the dom of a dynamic node is not generated when the page is loaded, the initialization debinding event cannot find the DOM node, so the management of the node is invalid. In order to solve this problem, we can find this node through the global click event, use the click of document to find this dynamic node, and then get the target of the current click, which is equal to the node found by xpath, indicating that the currently clicked node is the node to which xpath needs to bind the event. At this time, we can send the corresponding data

let { data } = await getConfig(route)
let eventList = data.filter((m) => m.eventType !== 'visible')
let viewList = data.filter((m) => m.eventType === 'visible')
let dynamicList = [] // Dynamically generated nodes
// Click event or other events
eventList.forEach((item) => {
  const node = item.xpath && document.evaluate(item.xpath, document).iterateNext()
  if (!node) {
    // The node cannot be found, indicating that it may be a dynamically generated node
  node.addEventListener(item.eventType || 'click', () => {
// Find dynamically generated nodes by clicking open of document
let dynamicClickList = dynamicList.filter((m) => m.eventType === 'click')
if (dynamicClickList && dynamicClickList.length) {
  document.onclick = (event) => {
    const target = ||
    const parentNode = target.parentNode
    for (let item of dynamicClickList) {
      // Save the found nodes first
      item.node = document.evaluate(item.xpath, document).iterateNext() || item.node
    let xpathItem = dynamicClickList.find((m) => {
      return m.xpath && (target === m.node || parentNode === m.node)
    // Find the node and send the dot
    xpathItem && delete xpathItem.node && action.track(xpathItem)

4. Target enters visual area

Usage scenario: some elements of horizontal scrolling switching need to be managed when the target enters the visible area of the user, so there is such a demand
IntersectionObserver reference document link

let observer = null // visible area 
let isTrackList = [] // Already ordered
if ('IntersectionObserver' in window) {
  // Create a listening node visual area
  observer = new IntersectionObserver(entries => {
    const image = entries[0]
    // Enter visual area
    if (image.isIntersecting) {
      // Management configuration of the current visual area
      let current = viewList.find((m) => m.xpath && === document.evaluate(m.xpath, document).iterateNext())
      // Already ordered
      let trackEd = isTrackList.find((m) => ===
      // No more points that have been hit
      if (current && !trackEd) {
viewList.forEach((item) => {
  const node = item.xpath && document.evaluate(item.xpath, document).iterateNext()
  if (!node) {
  // Listening node

Tags: Front-end Javascript Visualization SDK

Posted by leoric1928 on Fri, 25 Feb 2022 00:36:47 +1030