Building an Online Activity Tracker in JavaScript

Building an Online Activity Tracker in JavaScript

Capture and analyze user interactions for better UX

Are you looking to gain insights into how users interact with your website or web application? Having a comprehension of user conduct is significant for enhancing the overall experience and using data-oriented judgments. In this blog post, we'll walk you through the implementation of a user activity tracker in JavaScript, allowing you to monitor user interactions and gather valuable data.

Introduction

Understanding user behavior is essential for improving user experience and making data-driven decisions. To achieve this, there are many third party tracking application like Google Analytics which collect lots of analytics data and analysis. But today, we'll build our own user online activity tracker in JavaScript that captures various user interactions, such as clicks, mouse movements, and key presses, and sends this data to a server for analysis.

Implementation

Let's look at step by step guide to build our own online activity tracker from scratch.

  • Initialization: We'll initialize our tracking function name as Tracker which firstly checks if the document has focus.
OnlineTracker() {
    if (!document.hasFocus()) {
        return
    }
}

Here, if you want to go one step further and track activity of only your logged in users, then you can check whether user is logged in.

  • Event Listeners: The tracker will listen to several user events like click, mousemove, keydown, touchstart, and touchend. These events allow us to capture various user interactions.
const events = ["click", "mousemove", "keydown", "touchstart", "touchend"]

You can listen for other set of events if you would like to track other events also.

  • Data Storage: Before sending data to server, we'll use browser's localStorage to store tracked data temporarily. For data storage, initially user's online activity data will be stored in activityLog object. It will record information such as the current page URL, timestamp, log of user events like clicks count, etc. Then it is stored in localStorage.
activityLog.page = window.location.pathname
activityLog.page_full_url = window.location.href

activityLogString = JSON.stringify(activityLog, null, 2)

localStorage.setItem("activityLog", activityLogString)
  • Activity Log Reset: We'll add a reset function to clear the stored activity logs. This function will be called when the tracker initializes or after a certain inactive threshold is reached.
async function reset() {
    localStorage.removeItem("activityLog")
    clickCount = 0
    activityLog = setActivityLog()
}
  • Data Synchronization: Then, we'll add a syncData function to periodically sends the tracked activity log to the server for analysis. It will also check if the user has been inactive for an extended period and resets the activity log if necessary.
function syncData() {
    // check if not inactive until the limit
    if (inactiveThresholdCount < INACTIVE_THRESHOLD_LIMIT) {
        saveActivityLog(localStorage.getItem("activityLog"))
            .then(reset())
    }
}
  • Previous Activity Retrieval: Before sending data to server, a function getPreviousActivity will be added to retrieve the previous activity log from localStorage when the page loads. This ensures that we continue tracking user activity even after a page refresh.
function getPreviousActivity() {
    const previousString = localStorage.getItem("activityLog")
    if (previousString && previousString.trim() !== "") {
        activityLog = JSON.parse(previousString.trim())
        clickCount = activityLog.events.clicks.count
    }
}
  • Sending Data to the Server: Finally, the tracked user's online data will be sent to the server. This is done through saveActivityLog function. It will calculate the duration of the user's session and include it in the data payload. Here, we'll use fetch API but you can use HTTP request library of your choice.
async function saveActivityLog(activityLogString) {
    const requestData = (activityLogString && activityLogString.length) ? JSON.parse(activityLogString) : activityLog
    requestData.duration = parseInt((new Date() - new Date(requestData.startTime)) / 1000) || 0

    fetch.post("<URL>", requestData)
        .then(res => {})
        .catch(err => {
            console.log(err)
        })

    inactiveThresholdCount++
}

Full code

import { cloneDeep } from "lodash"

const SYNC_INTERVAL = 1000 * 60 * 10 // decide interval at which you want to sync data to server
const INACTIVE_THRESHOLD_LIMIT = 1 // set the threshold which will be used to check user's inactivity period

export default {
    OnlineTracker() {
        if (!document.hasFocus()) {
            return
        }

        const events = ["click", "mousemove", "keydown", "touchstart", "touchend"]
        let inactiveThresholdCount = 0
        let clickCount = 0
        const activityLogStructure = {
            page: window.location.pathname,
            page_full_url: window.location.href,
            events: {
                clicks: {
                    count: 0,
                    events: [],
                },
            },
            startTime: new Date(),
        }

        // set new activity object in case of new or after reset
        const setActivityLog = () => {
            const object = cloneDeep(activityLogStructure)
            object.startTime = new Date()
            return object
        }

        let activityLog = setActivityLog()
        let activityLogString

        async function saveActivityLog(activityLogString) {
            const requestData = (activityLogString && activityLogString.length) ? JSON.parse(activityLogString) : activityLog
            requestData.duration = parseInt((new Date() - new Date(requestData.startTime)) / 1000) || 0

            fetch("<URL>", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify(requestData),
            })
                .then(res => {})
                .catch(err => {
                    console.log(err)
                })

            inactiveThresholdCount++
        }

        async function reset() {
            localStorage.removeItem("activityLog")
            clickCount = 0
            activityLog = setActivityLog()
        }

        function syncData() {
            // check if not inactive until the limit
            if (inactiveThresholdCount < INACTIVE_THRESHOLD_LIMIT) {
                saveActivityLog(localStorage.getItem("activityLog"))
                    .then(reset())
            }
        }

        function getPreviousActivity() {
            const previousString = localStorage.getItem("activityLog")
            if (previousString && previousString.trim() !== "") {
                activityLog = JSON.parse(previousString.trim())
                clickCount = activityLog.events.clicks.count
            }
        }

        setInterval(function() {
            // send data on regular interval
            syncData()
        }, SYNC_INTERVAL)

        document.addEventListener("DOMContentLoaded", function() {
            // sync url and previous data (if exist) on DOM load
            getPreviousActivity()

            activityLog.page = window.location.pathname
            activityLog.page_full_url = window.location.href

            activityLogString = JSON.stringify(activityLog, null, 2)

            localStorage.setItem("activityLog", activityLogString)

            syncData()

            // listen for each event
            events.forEach(function(e) {
                document.addEventListener(e, function(evt) {
                    getPreviousActivity()

                    // if events not detected until threshold - consider user as inactive and renew time after event track started
                    if (inactiveThresholdCount >= INACTIVE_THRESHOLD_LIMIT) {
                        activityLog.startTime = new Date()
                    }
                    inactiveThresholdCount = 0

                    if (e === "click") {
                        activityLog.events.clicks.count = ++clickCount
                        const eventData = {
                            position: {
                                x: evt.x,
                                y: evt.y,
                            },
                            element: {
                                node: evt.target.nodeName,
                                id: evt.target.id,
                                className: evt.target.className,
                            },
                            text: evt.target.innerText || evt.target.value,
                            pageUrl: window.location.href,
                        }
                        activityLog.events.clicks.events.push(eventData)
                    }

                    activityLogString = JSON.stringify(activityLog, null, 2)

                    // save events in local storage
                    localStorage.setItem("activityLog", activityLogString)
                })
            })
        })
    },
}

Conclusion

In this blog post, we've introduced a JavaScript-based online user's activity tracker that captures user interactions and sends the data to a server for analysis. This tool can be invaluable for gaining insights into user behavior, identifying pain points in your web application, and making informed improvements.

By implementing an online user's activity tracker, you can enhance the user experience, optimize your website's performance, and ultimately achieve better engagement with your users. Remember to handle user data responsibly and in compliance with relevant privacy regulations.