DEV Community

Cover image for How to get time spent by users on a webpage using Node.js?
Kalpit Rathore
Kalpit Rathore

Posted on

How to get time spent by users on a webpage using Node.js?

Alt Text

Introduction

As the internet users increasing exponentially, It is very important for enterprises to know how a user interacts with their website so that they can improve user experience accordingly.

In this article, We will be going to talk about how to get time spent by users on a webpage using simple Javascript and Node.js. At first, We will try to understand the concept or working behind it then will implement it using code.

Working

Before deep dive into the code, Let's try to understand the working behind it with the help of the below flow chart.
Alt Text

Implementation

Let's get some hands dirty on code.

1) Create a project folder.

mkdir time-spent-by-user && cd time-spent-by-user
Enter fullscreen mode Exit fullscreen mode

2) Initialize npm in the folder.

npm init -y
Enter fullscreen mode Exit fullscreen mode

3) Install the required dependencies.

npm install --save express
npm install --save ejs
Enter fullscreen mode Exit fullscreen mode

4) Create an "app.js" file & write some code in it.

//app.js
const express = require('express');
const ejs = require('ejs');
const bodyParser= require('body-parser');
const app = express()
const port = 80

app.set('view engine', 'ejs');
app.use(bodyParser.json());

var analytics = {};

// It will render home page
app.get('/', (req, res) => {    
    res.render('index');
})
// ------------------------

// It will render dashboard page
app.get('/dashboard', (req, res) => {
    res.render('dashboard', {"analytics": analytics});
})
// ------------------------

// It will catch the data sent from "sendBeacon" method on home page
app.post('/updatetimespentonpage', bodyParser.text(), function(req,res){  
    var body = JSON.parse(req.body)    
    analytics[body["uid"]] = (analytics[body["uid"]]) ? (analytics[body["uid"]] + body["timeSpentOnPage"]) : (body["timeSpentOnPage"]);
    res.send({"status": "done"});
});

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`)
})
Enter fullscreen mode Exit fullscreen mode

5) Create a "views" folder.

mkdir views && cd views
Enter fullscreen mode Exit fullscreen mode

6) Create an "index.ejs" file in the views folder & write some code in it.

<!--index.ejs-->
<html>
   <head>
      <title>Home page</title>
   </head>
   <body>
      User Id: <span id="uid"></span> </br>
      Time spent on this page: <span id="time-spent">0s</span>
   </body>
   <script type="text/javascript">

    // Check if uid already exist in cookie.     
    if (!getCookie("uid")) {

        // if not, then create a new uid and store it in cookie.
        document.cookie = "uid=U" + (Date.now().toString(36)).toUpperCase() + "; expires=Thu, 18 Dec 2030 12:00:00 UTC; path=/";
    }
    // -------------------------------------------

    document.getElementById('uid').innerHTML = getCookie("uid");


    // This setInterval function increment the value of "timeSpent" variable each second.
    var timeSpent = 0;
    var timeSpentInterval = setInterval(
        function() {
            timeSpent++;
            document.getElementById('time-spent').innerHTML = timeSpent + "s";
        }, 1000);
    // ---------------------------------------------

    // The beforeunload event triggers right before unloading of the window has begun
    window.addEventListener("beforeunload", function() {

        // When user close or refresh the web page, sendBeacon method asynchronously sends a small amount of data over HTTP to a web server.
        navigator.sendBeacon('http://localhost/updatetimespentonpage', JSON.stringify({
            uid: getCookie("uid"),
            timeSpentOnPage: timeSpent
        }))
    });
    // ---------------------------------------------

    // Method used to get cookie
    function getCookie(cname) {
        var name = cname + "=";
        var decodedCookie = decodeURIComponent(document.cookie);
        var ca = decodedCookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') {
                c = c.substring(1);
            }
            if (c.indexOf(name) == 0) {
                return c.substring(name.length, c.length);
            }
        }
        return "";
    }
    // -----------------------------------
   </script>
</html>
Enter fullscreen mode Exit fullscreen mode

7) Create "dashboard.ejs" file & write some code in it.

<!--dashboard.ejs-->
<html>
    <head>
        <title>Dashboard</title>
        <style type="text/css">
            table, th, td {
                border: 1px solid black;
                border-collapse: collapse;
                padding: 10px;
            }
        </style>
    </head>
    <body>
        <h2>Dashboard</h2>
        <table>
            <tr>                
                <th>User Id</th>
                <th>Time Spent</th>
            </tr>            
                <%  
                    var total_time_spent = 0                    
                    for(i in analytics)
                    {
                        %>
                        <tr>
                            <td><%= i %></td>
                            <td><%= analytics[i] %>s</td>
                        </tr>
                        <%
                            total_time_spent = total_time_spent + analytics[i];
                    }
                %>
                <tr>
                    <%
                        if(Object.keys(analytics).length>0){
                    %>
                        <th>Total Users: <%= Object.keys(analytics).length %></th>
                        <th>Avg Time Spent: <%= (total_time_spent/Object.keys(analytics).length).toFixed(2) %>s</th>
                    <%
                        }
                        else{
                    %>
                        <td>NA</td>
                        <td>NA</td>
                    <%
                        }
                    %>
                </tr>            
        </table>
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

8) Execute the "app.js" file.

//if you are inside the views folder
cd ..
node app.js
Enter fullscreen mode Exit fullscreen mode

9) Open a browser & point to http://localhost, It will show you "UID" & time spent by you on the webpage.
Alt Text

10) Now, Close the browser tab & point to http://localhost/dashboard
Alt Text

11) Here you can see the list of all the users with their corresponding time spent on the page.

Conclusion

When the user closes or refreshes the home page, It fires the "beforeunload" event & lets the "sendBeacon" method send the time spent data to the server asynchronously. The server catches the data & store it in a variable (You can use a traditional database as well).

sendBeacon is intended to be used for sending analytics data to a web server, & avoids some of the problems with legacy techniques for sending analytics, Such as the use of XMLHttpRequest, Read more here

Check out the github code if you'd like to get sample implementation.

For more updates, follow me on Twitter.

Latest comments (7)

Collapse
 
saleemkce profile image
Saleem • Edited

Yes, beforeunload seems work in most mobile browsers but a combo of visibilityChange and beforeunload can be considered. Instead of building basic front end capture, you can straight away integrate the Timeonsite.js at client-side and NodeJs for saving the data to MongoDB. TimeOnSite JS for client-side capture natively across web and mobile browsers

Collapse
 
stefant123 profile image
StefanT123

Couldn't you just saved the time when the user opened the page in the cookie, and then when he leave the page, just calculate the difference between the time when he left, and the time when he opened the page? This way you will get the exact time spent on the site without the overhead that setInterval applies.

Collapse
 
kalpitrathore profile image
Kalpit Rathore

Yeah, We can achieve it in this way also

Collapse
 
inigochoa profile image
Iñigo Ochoa

Nice one!!

Consider using visibilitychange instead of beforeunload. Seems to work better on mobile phones.

developer.mozilla.org/en-US/docs/W...

Collapse
 
nikolicstjepan profile image
Nikolić Stjepan

Thanks for this useful article. Just the thing that I needed :)

Collapse
 
melfordd profile image
Melford Birakor

Thanks for the great content

Collapse
 
kalpitrathore profile image
Info Comment hidden by post author - thread only accessible via permalink
Kalpit Rathore

Thank you, it makes my day to hear that.

Some comments have been hidden by the post's author - find out more