preface

Why do we do this to monitor user stays? The reason is simple. If we want to analyze whether this page has a price for our products, then the browsing time of users is a key point. If each user stays on this page for more than two hours on average every day, we will think this page has a high value. If a page is not visited by many users for a month, then we have the question, is this page still valuable to our product? Do we want to keep it for the rest of our product? All of these requirements make us think about whether or not we should capture the duration of the user’s stay.

For what applications?

  • Multi-page application
  • Single-page applications (this article uses VUE as an example)

How do I get the duration of a user’s stay?

When monitoring functionality, our primary concern is that our monitoring code does not affect our current business code and future business code.

Multi-page application

In multi-page applications, it’s easy to get a user’s stay time. Check out the following apis 👇

  • Onload (after the page is loaded)

  • Onbeforeunload (before the page is unloaded, when the fork is clicked)

  • Onpageshow (when the page is displayed)

  • Onpagehide (when the page is hidden)

After a little testing of my own, I found that onPageHide is triggered either when it is closed (i.e. clicking the cross) or when it is hidden (i.e. clicking the left and right arrows). Onpageshow is triggered either when it’s first loaded or when it’s refreshed, but onPageHide is triggered first when it’s refreshed, and then onPagesHow is triggered. The other two apis are similar. In the end, I chose to use onPagesHow and onPageHide to get the duration of the user’s stay, but you can also use the other two apis. Just initialize the time value in onPagesHow, calculate the time value in onPageHide, and upload it to the background.


let stopTime

window.onpageshow = () = >{
  stopTime = new Date().getTime()
}

window.onpagehide = () = >{
  stopTime = new Date().getTime() - stopTime
  let record = localStorage.getItem('data')
  let data = record && JSON.parse(record) || []
  localStorage.setItem('data'.JSON.stringify([...data,{user:new Date().getTime(),path:window.location.href,stopTime}]))
}

Copy the code

The complete code is just that, here I just test, the simulated user ID, real path, real stay time stored in localStorage, in the project can be transmitted to the background, and then through analysis, and then visualized display.

Above rendering (posted a GIF is the AV quality, have no money to open the VIP, will see, feeling a bit AV, haha), first click on the fork, click on the fork that can also be understood as a left page, the second a link to jump, the third time refresh, refresh can also be understood as a leave, a coexistence of the three data, interested in try to better understand themselves.

Single page application

Single-page apps can be a little bit more complicated, but not much more complicated.

Route redirects for single-page applications are implemented based on H5 History API (browserHistory) and Hash (hashHistory).

browserHistory

The single-page browserHistory route is implemented based on the History API of H5. As long as we listen to popState, we can count how long the user has been on the page by clicking the forward and back buttons to change the URL

let timeStr

window.addEventListener('onload'.(e) = >{
  timeStr = new Date().getTime()
})

window.addEventListener('popstate'.() = >{
  let t = new Date().getTime() - timeStr
  timeStr = new Date().getTime()
  console.log('Length of stay:'+ t)
})
Copy the code

However, pushState and replaceState(i.e., router-view,$router-push,$router-. replace, window.history.pushState, Window. The history. ReplaceState not trigger, can try) will not trigger the popstate by oneself, that we can’t count the user long stay on the page; You can override pushState and replaceState, and then listen for the two custom events. See 👇


// Extend the function
let rewriteHis = function(type){
  let origin = window.history[type] // Store the original function
  return function(){ // This function is executed when the window.history[type] function is executed
    let rs = origin.apply(this.arguments) // Execute the original function
    let e = new Event(type.toLocaleLowerCase()) // Define a custom event
    e.arguments = arguments // Bind the default arguments to custom events. New Event returns the result, which has no arguments on its own
    window.dispatchEvent(e) // Fire the custom event to pass the payload to the custom event
    return rs
  }
}

window.history.pushState = rewriteHis('pushState') // Override the pushState method

window.history.replaceState = rewriteHis('replaceState') // Override the original replaceState method

// Listen for custom events. Pushstate events are registered with rewriteHis, not native events
// Router-link or window.history.pushState or this.$router-push will be listened to by this event
window.addEventListener('pushstate'.() = >{})

// Listen for custom events. Replacestate events are registered with rewriteHis, not native events
/ / when click window. History. ReplaceState or enclosing $router. When the replace will be listening to the event
window.addEventListener('replacestate'.() = >{})

Copy the code

The rewriteHis function, which is basically an extension of the original function, should be made clear in the comments above.

BrowserHistory Route change monitor complete code 👇


let timeStr

let rewriteHis = function(type){
  let origin = window.history[type]
  return function(){
    let rs = origin.apply(this.arguments)
    let e = new Event(type.toLocaleLowerCase())
    e.arguments = arguments
    window.dispatchEvent(e)
    return rs
  }
}

window.history.pushState = rewriteHis('pushState')

window.history.replaceState = rewriteHis('replaceState')

window.addEventListener('onload'.(e) = >{
  timeStr = new Date().getTime()
})

window.addEventListener('popstate'.() = >{
  let t = new Date().getTime() - timeStr
  timeStr = new Date().getTime()
  console.log('PopState:'+ t)
})

window.addEventListener('pushstate'.() = >{
  let t = new Date().getTime() - timeStr
  timeStr = new Date().getTime()
  console.log(Pushstate: '+ t)
})

window.addEventListener('replacestate'.() = >{
  let t = new Date().getTime() - timeStr
  timeStr = new Date().getTime()
  console.log('Replacestate:'+ t)
})

Copy the code

The demo here will not do any storage, like, do their own 👇

hashHistory

HashHistory is incredibly simple. Just listen on Hashchange


window.addEventListener('hashchange'.() = >{
  let t = new Date().getTime() - timeStr
  timeStr = new Date().getTime()
  console.log('Length of stay:'+ t)
})


Copy the code

So far, how do single-page apps, multi-page apps capture how long users are on that page? I don’t really understand. It’s pretty simple.

subsequent

You think that’s it? There’s another weird question.

const router = new VueRouter({
  mode:'hash'.routes: [...]. })Copy the code

AddEventListener (‘hashchange’,()=>{}); The window.addEventListener(‘replacestate’,()=>{} and window.addEventListener(‘ pushState ‘,()=>{}) History API are generated. Why is the History API triggered when I set it to hash route? With this question, I could not help but go to see the vue-Router source code, finally, unlock their own questions, look at the following:

Hash route implementation file in vue-router 👇

If supportsPushState is false,else logic will trigger window.addeventListener (‘hashchange’,()=>{}). What is supportsPushState? PushState,replaceState? Why does it trigger custom events?

supportsPushState

Let’s see what supportsPushState is 👇

SupportsPushState: supportsPushState: supportsPushState: supportsPushState: supportsPushState: supportsPushState: supportsPushState: supportsPushState Else statement (UA.indexof (‘Android 2.’)! = = 1 | | ua. IndexOf (” Android 4.0 “). == -1) && ua.indexOf(‘Mobile Safari’) ! == -1 &&UA. indexOf(‘Chrome’) == -1 &&UA. indexOf(‘Windows Phone’) === -1 otherwise all other routers are Hash routers based on the History API.

PushState, replaceState

Take a look at the implementation of the two apis 👇

It turns out that they both call the History API implementation, which explains why it fires custom events.

See the source guide 👇

If you don’t believe me, I can look at the source code.

The last

The above is for individuals to obtain the length of time the user stays on multiple pages or single pages. If there is a better method, welcome to exchange. 🐶 potentially life-saving)