Building and sending events with wem.js

November 14, 2023

Overview

Embedded within a site at page load time, the wem.js library is used to send events to jCustomer and ships with a default configuration applied when the tracker is enabled on a page. If there is a need for additional logic and events to be tracked, wem.js functions can be called directly.

Before starting and to best understand how events are sent and processed, please read through the “key concepts” section on the Academy. 

Enabling or Disabling wem.js

Using jExperience UI

By default, when jExperience is active on your site, it collects visitor's behavioral data (for example, page views). This is done with a Javascript tracker (wem.js) that send events to jCustomer. For different reasons, you may want to disable the jExperience tracking or use a 3rd party solution, like a consent manager or a tag manager to enable tracking. 

To disable Javascript tracking by default:

  1. Navigate to your site in Page Composer and open the site for editing.
  2. In Options, enable the Execute jExperience Javascripts from a 3rd party solution mixin and select Current content and all sub content.
    load-jexperience-tag-manager.png

This option is also available for any page, so that you can decide to disable the Javascript tracking for a specific section of your website or for a single page.

Once you save, page view events are no longer sent to jCustomer until the wem is enabled again.

Using Javascript

When jExperience is active on a site, each page rendered in live has a Javascript object named wem. It is available once the document is loaded.

Note: If jExperience tracking is disabled, the wem object will still be available, but data won't be send to jCustomer.

You can use the following self speaking commands to activate or deactivate the tracking:
wem.enableWem() 
wem.disableWem()

Library definition

The section below details in Typescript notation the wem.js functions detailed in this document and which you’re the most likely to use.

declare namespace wem {
    interface Properties {
        [key: string]: any;
    }

    interface Item {
        scope: string;
        itemId: string;
        itemType: string;
        properties: Properties;
    }

    interface Event {
        eventType: string;
        scope: string;
        source: Item;
        target: Item;
    }

    interface Events {
       events: Event[]
    }

    function buildSource(id: string, type: string, properties: Properties): Item; 
    function buildTarget(id: string, type: string, properties: Properties): Item;

    function buildSourcePage(): Item; 
    function buildTargetPage(): Item;

    function buildEvent(type: string, target: Item, source: Item): Event; 

    function collectEvents(events: Events, successCallback: void, errorCallback: void): undefined

    function enableWem(): undefined
    function disableWem(): undefined
}

Properties

Properties are elements aimed at being stored alongside the “Item” in an event. 

Although jCustomer offers flexibility around the use (or not) of properties, it is recommended to use properties for the actual elements to be acted upon.

In most situations, properties will be coming from the “page” object in “window.digitalData”, and are populated automatically when using “buildSourcePage()” or “buildTargetPage()”.

Properties validation check

The property must be allowed by jCustomer before being sent. A property is validated on jCustomer side by a JSON schema that allows the property.

The JSON schema must be registered before sending any property.

See Unomi documentation regarding JSON schema for more detail.

Go to the the Deploy a JSON schema from a Jahia module section to know more about registering JSON schemas from Jahia.

Create Items

At the core of how jCustomer processes and stores events, the Item is used to describe either the event’s source or target. Four functions are available to generate Items:

buildSource() and buildTarget() are interchangeable and can be used to generate an item by specifying an id, a type and some properties. The “scope” property is populated automatically using the value of window.digitalData.scope

buildSourcePage() and buildTargetPage(), also interchangeable, automatically generate an Item of type page and pre-populate it with page elements coming from window.digitalData.page. 

Implementation of the buildSourcePage() function:

buildSourcePage: function () {
    return wem.buildSource(window.digitalData.page.pageInfo.pageID, 'page', window.digitalData.page);
}

In most situations and when needing to customize the behavior, you will be using buildSourcePage() to create the event’s source and buildTarget() to create the event’s target.

Code sample

const newSource = wem.buildSourcePage()
console.log(newSource) 
/*
Sample generated object:
{
    "scope": "digitall",
    "itemId": "6077ca81-46bf-4fb5-8811-24ea1c90413b",
    "itemType": "page",
    "properties": {
        "pageInfo": {
            "pageID": "6077ca81-46bf-4fb5-8811-24ea1c90413b",
            "nodeType": "jnt:page",
            "pageName": "Home",
            "pagePath": "/sites/digitall/home",
            "templateName": "home",
            "destinationURL": "http://localhost/sites/digitall/home.html",
            "destinationSearch": "",
            "referringURL": null,
            "language": "en",
            "categories": [],
            "tags": [],
            "isContentTemplate": false,
            "sameDomainReferrer": false
        },
        "attributes": {},
        "consentTypes": []
    }
}
*/
const newTarget = wem.buildTarget('startQuizzLink', 'a')
console.log(newTarget)
/*
Sample generated object
{
    "scope": "digitall",
    "itemId": "startQuizzLink",
    "itemType": "a"
}
*/

Create an Event

An Event corresponds to an actual element to be sent and stored in jCustomer/Unomi.

Code sample

In the following code sample we’re creating an event of type “click”.
 

const source = wem.buildSourcePage()
const target = wem.buildTarget('startQuizzLink', 'a')
const newEvent = wem.buildEvent('click', target, source)
console.log(newEvent) // => {eventType: 'click', scope: 'digitall', target: {...}, source: {...}}

Send Events

A generic function, called collectEvents can be used to submit any type of events to jCustomer.

The collectEvents function take three parameters:

  • events: (required) object containing events to be submitted to the backend.
  • successCallback: (optional) function to be called after a successful submission
  • errorCallback: (optional) function to be called after a failed submission

The two callback functions can, for example, be useful if you’re submitting your events via a queuing mechanism, you could use them to clear successfully sent events from the queue or retry submission in a few seconds if the remote server was busy.

Code sample

In the following code sample we’re creating a target item of type “a” with the id “startQuizzLink”, and are sending it to our backend.

// Creation of the target and source items
const source = wem.buildSourcePage()
const target = wem.buildTarget('startQuizzLink', 'a')

// Creation of the event
const newEvent = wem.buildEvent('click', target, source)

// Creation of the two callback functions
const successfulSubmission = () => {console.log('Submission of the event was successful')}
const failedSubmission = () => {console.log('Submission of the event failed')}

// Submission of the event
wem.collectEvents({events: [newEvent]}, successfulSubmission, failedSubmission)

Executing this code block will result in the corresponding event being sent to jCustomer.

Use tracked conditions to automatically trigger events

When fetching the context.json from jCustomer, wem.js will automatically create listeners based on elements contained in the trackedConditions array.

The wem.js library supports the following conditions types:

  • formEventCondition
  • videoViewEventCondition
  • clickOnLinkEventCondition
  • clickEventCondition
     

Using formEventCondition

When loading the page, the wem.js library will be looking for any <form> element (using document.querySelectorAll('form') ) with a name attribute matching “parameterValues.formId”.

For example, the following trackedConditions returned by the context.json endpoint in jCustomer: 
 

"trackedConditions": [{
    "parameterValues": {
        "formId": "searchForm"
    },
    "type": "formEventCondition"
}]

Will automatically result in the following forms to be watched (see the form name matching the formId), and will trigger an event to be sent when submitting the form:

<form method="post" name="searchForm" action="/sites/virtuall/home/search-results.html">    
  <input type="hidden" name="jcrMethodToCall" value="get">
  <input name="src_terms[0].term" type="text" >
</form>

The even being sent will then attach <textarea />, <input /> and <select> form elements to the target properties.

Using videoViewEventCondition

When loading the page, the wem.js library will be looking for all dom elements matching the name of the video specified in “parameterValues.videoId”. 

The finding of the video is based on the following code in wem.js

document.getElementById(videoName) || document.getElementById(wem._resolveId(videoName))

And once the video element has been identified, it will add a listener on the “play” and “ended” video events.

For example, the following trackedConditions returned by the context.json endpoint in jCustomer:

"trackedConditions": [{
    "parameterValues": {
        "videoId": "my-awesome-video"
    },
    "type": "videoViewEventCondition"
}]

Will automatically trigger events whenever “my-awesome-video” is played or stopped.

The event being sent uses the video as its target, using “{ action: event.type }” for its target properties.

Using clickOnLinkEventCondition or clickEventCondition

When loading the page, the wem.js library will be looking for all dom elements matching the name specified in “parameterValues.itemId”.

Both clickOnLinkEventCondition and clickEventCondition are interchangeable.

The finding of the video is based on the following code in wem.js

(document.getElementById(clickIdName) || document.getElementById(wem._resolveId(clickIdName)))
                    ? (document.getElementById(clickIdName) || document.getElementById(wem._resolveId(clickIdName)))
                    : document.getElementsByName(clickIdName)[0]

For example, the following trackedConditions returned by the context.json endpoint in jCustomer: 

"trackedConditions": [{
    "parameterValues": {
        "itemId": "link-to-google"
    },
    "type": "clickOnLinkEventCondition"
}]

Will automatically trigger events whenever the “link-to-google” element is clicked.