User Behavior Tracking w/ JavaScript (P2)
Tags: javascript, programming, python, tutorial
Published: 2021-03-05 18:13:56

{% youtube 91NclqtDt6c %}
Hello DEV community! It's been a while since my first post on this project. [Here](https://dev.to/romverner/making-a-user-behavior-tracking-app-w-javascript-p1-37fi) 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:

- [click](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event)
- [auxclick](https://developer.mozilla.org/en-US/docs/Web/API/Element/auxclick_event)
- [focus](https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event)
- [blur](https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event)
- [mouseover](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseover_event)
- [mouseleave](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event)

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](https://developer.mozilla.org/en-US/docs/Glossary/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](https://github.com/romverner/HAWK).

**`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);
    };
};
```

**`_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;
    };
};
```

**`_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
{% github romverner/HAWK %}

Thank you for taking the time to read my post!