{"id":300072,"date":"2019-12-13T07:47:25","date_gmt":"2019-12-13T14:47:25","guid":{"rendered":"https:\/\/css-tricks.com\/?p=300072"},"modified":"2019-12-13T07:47:25","modified_gmt":"2019-12-13T14:47:25","slug":"detecting-inactive-users","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/detecting-inactive-users\/","title":{"rendered":"Detecting Inactive Users"},"content":{"rendered":"
Most of the time you don\u2019t really care about whether a user is actively engaged or temporarily inactive on your application. Inactive, meaning, perhaps they got up to get a drink of water, or more likely, changed tabs to do something else for a bit. There are situations, though, when tracking the user activity and detecting inactive-ness might be handy.<\/p>\n
Let\u2019s think about few examples when you just might need that functionality:<\/p>\n
<\/p>\n
I recently encountered a feature that involved that last example, auto logging out inactive users for security reasons.<\/p>\n
Many applications give users access to some amount of their personal data. Depending on the purpose of the application, the amount and the value of that data may be different. It may only be user\u2019s name, but it may also be more sensitive data, like medical records, financial records, etc.<\/p>\n
There are chances that some users may forget to log out and leave the session open. How many times has it happened to you? Maybe your phone suddenly rang, or you needed to leave immediately, leaving the browser on. Leaving a user session open is dangerous as someone else may use that session to extract sensitive data.<\/p>\n
One way to fight this issue involves tracking if the user has interacted with the app within a certain period of time, then trigger logout if that time is exceeded. You may want to show a popover, or perhaps a timer that warns the user that logout is about to happen. Or you may just logout immediately when inactive user is detected.<\/p>\n
Going one level down, what we want to do is count the time that\u2019s passed from the user\u2019s last interaction. If that time period is longer than our threshold, we want to fire our inactivity handler. If the user performs an action before the threshold is breached, we reset the counter and start counting again.<\/p>\n
This article will show how we can implement such an activity tracking logic based on this example<\/a>.<\/p>\n Let\u2019s implement two functions. The first will be responsible for resetting our timer each time the user interacts with the app, and the second will handle situation when the user becomes inactive:<\/p>\n OK, so we have methods responsible for tracking the activity but we do not use them anywhere yet.<\/p>\n Now we need to implement methods that are responsible for activating the tracking. In those methods, we add event listeners<\/a> that will call our That\u2019s it. Our user tracking is ready. The only thing we need to do is to call the We can leave it like this, but if you look closer, there is a serious performance issue with the code we just committed. Each time the user interacts with the app, the whole logic runs. That\u2019s good, but look closer. There are some types of events that are fired an enormous amount of times when the user interacts with the page, even if it isn\u2019t necessary for our tracking. Let\u2019s look at Let\u2019s do that now.<\/p>\n First, we need to add one more variable that will keep reference to our throttler timeout.<\/p>\n Then, we create a method that will create our throttler. In that method, we check if the throttler timeout already exists, and if it doesn\u2019t, we create one that will fire the We just created a new method that should be fired on user interaction, so we need to remember to change the event handlers from Now that we have our activity tracking logic implemented let\u2019s see how can move that logic to an application build with Vue. We will base the explanation on this example<\/a>.<\/p>\n First we need to move all variables into our component\u2019s Then we move all our functions to Since we are using Vue and it\u2019s reactive system, we can drop all direct DOM manipulations i.(i.e. Last thing we need to do is to find a proper place to activate the tracking logic. Vue comes with component lifecycle hooks<\/a> which are exactly what we need \u2014 specifically the Step 1: Implement tracking logic<\/h3>\n
\n
resetUserActivityTimeout<\/code> – This will be our method that\u2019s responsible for clearing the existing timeout and starting a new one each time the user interacts with the application.<\/li>\n
inactiveUserAction<\/code> – This will be our method that is fired when the user activity timeout runs out.<\/li>\n<\/ul>\n
let userActivityTimeout = null;\r\n\r\nfunction resetUserActivityTimeout() {\r\n clearTimeout(userActivityTimeout);\r\n userActivityTimeout = setTimeout(() => {\r\n inactiveUserAction();\r\n }, INACTIVE_USER_TIME_THRESHOLD);\r\n}\r\n\r\nfunction inactiveUserAction() {\r\n \/\/ logout logic\r\n}<\/code><\/pre>\n
Step 2: Tracking activation<\/h3>\n
resetUserActivityTimeout<\/code> method when the event is detected. You can listen on as many events as you want, but for simplicity, we will restrict that list to a few of the most common ones.<\/p>\n
function activateActivityTracker() {\r\n window.addEventListener(\"mousemove\", resetUserActivityTimeout);\r\n window.addEventListener(\"scroll\", resetUserActivityTimeout);\r\n window.addEventListener(\"keydown\", resetUserActivityTimeout);\r\n window.addEventListener(\"resize\", resetUserActivityTimeout);\r\n}<\/code><\/pre>\n
activateActivityTracker<\/code> on our page load.<\/p>\n
mousemove<\/code> event. Even if you move your mouse just a touch,
mousemove<\/code> event will be fired dozens of times. This is a real performance killer. We can deal with that issue by introducing a throttler that will allow the user activity logic to be fired only once per specified time period.<\/p>\n
Step 3: Improve performance<\/h3>\n
let userActivityThrottlerTimeout = null<\/code><\/pre>\n
resetUserActivityTimeout<\/code> after specific period of time. That is the period for which all user activity will not trigger the tracking logic again. After that time the throttler timeout is cleared allowing the next interaction to reset the activity tracker.<\/p>\n
userActivityThrottler() {\r\n if (!userActivityThrottlerTimeout) {\r\n userActivityThrottlerTimeout = setTimeout(() => {\r\n resetUserActivityTimeout();\r\n\r\n clearTimeout(userActivityThrottlerTimeout);\r\n userActivityThrottlerTimeout = null;\r\n }, USER_ACTIVITY_THROTTLER_TIME);\r\n }\r\n}<\/code><\/pre>\n
resetUserActivityTimeout<\/code> to
userActivityThrottler<\/code> in our activate logic.<\/p>\n
activateActivityTracker() {\r\n window.addEventListener(\"mousemove\", userActivityThrottler);\r\n \/\/ ...\r\n}<\/code><\/pre>\n
Bonus: Let\u2019s reVue it!<\/h3>\n
data<\/a><\/code>, that is the place where all reactive props live.<\/p>\n
export default {\r\n data() {\r\n return {\r\n isInactive: false,\r\n userActivityThrottlerTimeout: null,\r\n userActivityTimeout: null\r\n };\r\n },\r\n\/\/ ...<\/code><\/pre>\n
methods<\/a><\/code>:<\/p>\n
\/\/ ...\r\n methods: {\r\n activateActivityTracker() {...},\r\n resetUserActivityTimeout() {...},\r\n userActivityThrottler() {...},\r\n inactiveUserAction() {...}\r\n },\r\n\/\/ ...<\/code><\/pre>\n
document.getElementById(\"app\").innerHTML<\/code>) and depend on our
isInactive<\/code> data property. We can access the data property directly in our component\u2019s template like below.<\/p>\n
<template>\r\n <div id=\"app\">\r\n <p>User is inactive = {{ isInactive }}<\/p>\r\n <\/div>\r\n<\/template><\/code><\/pre>\n
beforeMount<\/a><\/code> hook. So let\u2019s put it there.<\/p>\n
\/\/ ...\r\n beforeMount() {\r\n this.activateActivityTracker();\r\n },\r\n\/\/ ...<\/code><\/pre>\n