DEV Community

Roman Verner
Roman Verner

Posted on

User Behavior Tracking w/ JavaScript (P2)


Hello DEV community! It's been a while since my first post on this project. Here is a link in case you missed part one, but if you haven't read the original post, no worries. We're going to rewrite and refactor the original code to better suit our needs moving forward. I took a hiatus from my personal projects after starting a new job, but this project in particular has me excited to start back up again.

Quick Recap:

We're trying to create a front-end JS module that will allow us to track user behavior as it relates to interacting with our online forms. The intent being that we can proactively discover and correct confusing areas in said forms, and improve the overall experience for the end user; however, this isn't the only application of our app. Once we setup a robust tracking system, we can even detect more abstract behaviors, such as an online shopper hovering over the "Purchase" button and hesitating before officially submitting their order.

Refactoring:
It's been a while since the last post so I decided to restart the project and introduce some slightly different and refactored logic. All of this is covered in the embedded video as well as the code snippets below.

Tracking By Element:
The first thing we need to acknowledge is that each HTML element that we want to track has different types of interactivity. For example, users typically can't focus on span elements, since span elements don't contain a way for users to input values. The five main events we'll record are:

To track interactions with button elements we might use mouseover, mouseleave, and click. To track input elements, we might use focus, blur, mouseover, and mouseleave.

Since different elements will use different events to record behavior, we need to create tracking functions specifically tailored to each element. If we discover that we're coding too repetitively, we can refactor down the line. So, let's start with our tracking functions.

Just as we did in the previous post, these functions are going to be contained within an IIFE. Let's outline some of our most important functions.

Code Overview

For the full context behind the functions listed below, please visit the GitHub repository.

HAWK.track(elementId)
This function will take in an element's ID and automatically setup the necessary event listeners for that element type. We can get the element type using the nodeName property, which returns string values such as 'SPAN', 'DIV', etc.. This is one of the first changes as compared to the first post in this series. We are now using the nodeName property. This will allow us to create a controller function called _addListeners that's effectively one big switch statement referencing the specific element type listener functions.

const _track = (id) => {
    let elementRef = _d.getElementById(id);
    let elementType = elementRef.nodeName;
    if (_checkConditions(id, elementType)) {
        _data.trackers[id] = _createTrackObj(id, elementType, elementRef);
        _addListeners(id, elementType, elementRef);
    };
};
Enter fullscreen mode Exit fullscreen mode

_addListeners(elementId, elementReference, nodeName)
This function takes in the element ID, the element's HTML reference (aka the product of document.getElementById()), and the nodeName. Technically we don't need the second and third parameters as we could just reacquire them within the scope of the function, but we already gather that info from the track() function, so it's easier to just pass it down.

const _addListeners = (id, type, reference) => {
    switch (type) {
        case _elTypes.button:
            reference.addEventListener(_eventTypes.c, () => {
                _clickListen(id);
            });
            Object.keys(_eventTypes.m).forEach((key, i) => {
                reference.addEventListener(_eventTypes.m[key], () => {
                    _mouseListen(id, i);
                });
            });
            break;

        case _elTypes.span:
            Object.keys(_eventTypes.m).forEach((key, i) => {
                reference.addEventListener(_eventTypes.m[key], () => {
                    _mouseListen(id, i);
                });
            });
            break;

        case _elTypes.input:
            Object.keys(_eventTypes.m).forEach((key, i) => {
                reference.addEventListener(_eventTypes.m[key], () => {
                    _mouseListen(id, i);
                });
            });
            Object.keys(_eventTypes.fb).forEach((key, i) => {
                reference.addEventListener(_eventTypes.fb[key], () => {
                    _focusListen(id, i);
               });
            });
            break;

        default:
            break;
    };
};
Enter fullscreen mode Exit fullscreen mode

_focusListen, _mouseListen, _clickListen
These functions store the action to be performed on each event. Since every element may require different types of event listeners, we code most of our repetitive code here. This is one of the few areas that I want to re-factor down the line, but I'd rather focus on getting to a minimum viable product first.

Code

HAWK

This repository is setup to run/test right out of the gate. Simply pull the repo, open up the index.html file in your preferred browser, and open up your developer console to interact with the HAWK module. HAWK.results() will console log all tracked events.

Tracking Elements

The HAWK.track function takes in an element ID as its parameter. It will automatically attach any listeners based on what type of element it is. As of the time of this writing, the only tracked elements are SPAN's, INPUT's, and BUTTON's.

Thank you for taking the time to read my post!

Top comments (0)