Tagging
You can implement all sorts of front-end user events easily with walker.js. From product and UX events like promotion view, filter usage, etc. to e-commerce actions like product add to carts or order complete events. The walker.js handles all trigger initializations and race conditions, builds the events with context, and distributes them based on consent states and mapping definitions to any destinations.
Tag a page with...
<!-- Generic usage -->
<div
data-elb="ENTITY"
data-elb-ENTITY="KEY:VALUE"
data-elbaction="TRIGGER:ACTION"
data-elbcontext="KEY:VALUE"
data-elbglobals="KEY:VALUE"
/>
<!-- Example usage -->
<div data-elbglobals="language:en">
<div data-elbcontext="test:engagement">
<div data-elb="promotion" data-elbaction="visible:view">
<h1 data-elb-promotion="name:Setting up tracking easily">
Setting up tracking easily
</h1>
<p data-elb-promotion="category:analytics">Analytics</p>
</div>
</div>
</div>
... to get a full event like the following as a result:
{
event: 'promotion view', // Name as a combination of entity and action
data: {
// Arbitrary properties related to the entity
name: 'Setting up tracking easily',
category: 'analytics',
},
context: {
// Provides additional information about the state during the event
test: ['engagement', 0] // Key, [value, order]
},
globals: {
// General properties that apply to every event
language: 'en'
},
custom: {}, // Additional space for individual setups
user: {
// Contains user identifiers for different identification levels
// Require consent and set manually for sessions building and cross-device
id: 'us3r1d',
device: 'c00k131d',
session: 's3ss10n1d',
},
nested: [], // List of nested entities
consent: { functional: true }, // Status of the granted consent state(s)
id: '1647261462000-01b5e2-5', // Timestamp, group & count of the event
trigger: 'visible', // Name of the trigger that fired
entity: 'promotion', // The entity name involved in the event
action: 'view', // The specific action performed on the entity
timestamp: 1647261462000, // Time when the event fired
timing: 3.14, // Duration how long it took to trigger this event
group: '01b5e2', // Random identifier for all events during a run
count: 2, // Incremental counter of the events in the same run
version: {
// Information about the used implementation setup
source: 'X.X.X', // Semantic version of the used source
tagging: 42, // A version number of the then-used tagging status
},
source: {
// Details about the origin of the event
type: 'web', // Source type of the event (also app, server, or custom one)
id: 'https://github.com/elbwalker/walkerOS', // Source of the event's origin
previous_id: 'https://www.elbwalker.com/' // Previous source (like referrer)
}
}
Learn more about the event model.
You are entirely free to define naming conventions. All you need to get started are the entity, action & trigger attributes. Learn more about the event model in our blog.
- You define the entity scope by setting the data-elb attribute with the
name of an entity to an element, e.g.,
data-elb="promotion"
. The default entity ispage
when nodata-elb
is set. - An action can be added by setting the data-elbaction attribute on the
same level or all child elements in combination with a matching
trigger, e.g.,
data-elbaction="visible:view"
to fire a promotion view event when the element has been in the viewport for at least 50% for one second. - (Optional) To define the entities' properties, set the composited
attribute data-elb-ENTITY with the name and value, e.g.
data-elb-promotion="name:Setting up tracking easily;position:overlay"
.
Trigger
Walker.js comes with a bunch of pre-built triggers. You don't have to deal with event listener or mutation observer initialization anymore.
Event | Definition |
---|---|
load | after loading a page when the DOM is ready |
click | when the element or a child is clicked |
visible | after the element has been in the viewport for at least 50% for one second |
hover | each time the mouse enters the corresponding element |
submit | on a valid form submission |
wait(ms) | waits ms seconds (15 seconds by default) until triggering |
pulse(ms) | recurring trigger every ms seconds (15 seconds by default) if the page is not hidden |
scroll(y) | fires when min. y percent of the element's height is above the bottom of the window, e.g., scroll(80) for an 80% threshold |
Trigger names are predefined and need to be selected from the list, while the
action
can be an arbitrarily defined name.
Abbreviation
If the trigger and action values are equal, e.g., for click events, you can just shorten the implementation:
<b data-elbaction="click">
is equal to <s data-elbaction="click:click">long</s>
</b>
Parameters
Some triggers require more information during initialization, while others accept optional parameters. A scroll trigger needs to know the percentage a user scrolls down while a wait trigger wants to know the number of milliseconds until the action gets triggered. Use brackets behind the trigger to pass that information.
<!-- specifying trigger parameters -->
<p data-elbaction="wait(10):interested"></p>
<p data-elbaction="pulse(10):interested"></p>
Action filter
At some point, you might want to nest an entity inside another. To prevent an action to trigger both entities, you can restrict the action to a specific entity by adding the name, e.g. data-elbaction="load:view(product)". If the trigger event gets called, the result will only include the property values from the specific entities.
<!-- setting a filter for an entity -->
<div data-elb="foo">
<div data-elb="bar" data-elbaction="load:hello(bar)">
only the bar hello event fires.
</div>
</div>
Up-bubbling click trigger
By clicking an element, the event bubbles up to the body element. If the walker finds a data-elbaction with the click trigger, it will fire the action. Often, the image or a whole div-block gets clicked, not the parent a-element. Using the bubbling-up flow, the walker still triggers the actions for you.
<button data-elb="product" data-elbaction="click">
<img class="full" src="some.jpg" alt="" />
</button>
The click trigger uses the bubbling-up process. It will not work with stopPropagation or preventDefault.
Linking elements
Use the data-elblink
tag to extend the scope of an entity by other elements
placed somewhere else (like modals). Specific IDs connect linked elements. They
are hierarchically and can either be a parent or a child.
<div data-elb="info" data-elblink="details:parent">...</div>
...
<div data-elblink="details:child" data-elbaction="visible">...</div>
<p data-elblink="another:child">...</p>
The second element is the parent, triggering the visible action for the
info visible
event. There can be multiple children, but there is only one
parent element per ID.
data-elb
, data-elbaction
, data-elbcontext
, data-elbglobals
, and
data-elblink
are reserved attributes, whereas data-elb-*
attributes may be
arbitrary combinations based on the related entity name. Actions and properties
can be set anywhere inside an elb scope.
Spaces in entities, e.g., "shopping cart" or actions, e.g., "add to cart" will be replaced by underscores to "shopping_cart" and "add_to_cart".
Spaces in property values are no problem, e.g. "category: 'summer sale'"
works
fine. But it is better to set them in quotes when doing so or when using
symbols, especially :
or ;
Data
Basic attributes
To specify data
, use the name of the entity. The data
attributes have to be
inside of the entity scope or a parent.
<div data-elb-entity="source:parent">
<div data-elb="entity">
<p data-elb-entity="key:value">...</p>
<p data-elb-entity="foo:bar">...</p>
</div>
</div>
{ data: { source: "parent", key: "value", foo: "bar", } }
Hierarchy
There is a hierarchy for the data
properties, where the order defines which
values to use for similar keys. Based on the triggering action element, the
closest ones or parent values will be preferred over the others.
<div id="family" data-elb="e" data-elbaction="click">
<div id="parent" data-elb-e="key:foo" data-elbaction="click">
<p id="child" data-elb-e="key:bar" data-elbaction="click"></p>
<b id="sibling" data-elbaction="click"></b>
</div>
<b data-elb-e="key:baz"></b>
</div>
Based on which element gets clicked, the event will contain the following data
- family:
{ key: 'baz' }
, the last found data-property - parent:
{ key: 'foo' }
, a direct data-value - child:
{ key: 'bar' }
, direct value closer than the parent - sibling:
{ key: 'foo' }
, no value specified, so it takes the parent's value
Type casting
Property values will be cast to their type, supporting string, number & boolean.
<div data-elb="types">
<p data-elb-types="string:text">{ string: "text" }</p>
<p data-elb-types="int:42;float:3.14">{ int: 42, float: 3.14 }</p>
<p data-elb-types="bool:true">{ bool: true }</p>
</div>
Multiple attributes
Browsers override duplicate attributes. Hence an element can only have one
data-elb
, data-elb-ENTITY
, and/or data-elbaction attribute at a time.
Nevertheless, it’s possible to define multiple entities, properties, and/or
actions within one attribute using quotes and semicolons. A semicolon splits
key-value pairs. Therefore, it’s necessary to escape values that contain a
semicolon. Quotes are here to meet your needs. To prevent a mistaken
value-split, use single quotes.
<!-- value wrapping with quotes -->
<p data-elb="foo" data-elb-foo="b:a;r">{ "b": "a", "r": true }</p>
<p data-elb="foo" data-elb-foo="b:'a;r'">{ "b": "a;r" }</p>
If a single quote is part of the value, escape it with a backslash:
<!-- escaping values with backslash -->
<p data-elb="foo" data-elb-foo="bar:it\'s escaped">{ "bar": "it's escaped" }</p>
The semicolon can be used as a separator to list multiple values inside of a
data-elb
or data-elbaction
attribute.
<!-- using multiple key-value pairs at once -->
<p data-elb="foo" data-elb-foo="a:1;b:2">{ "a": 1, "b": 2 }</p>
Dynamic field values
You might want to measure dynamic field values, e.g. the quantity of a product
or the value of the selected element. Use a #
at the beginning, followed by
the attribute name to access the value of the element attribute.
<!-- Basic usage: elb-ENTITY="KEY:#VALUE" -->
<input type="text" value="blue" data-elb-product="color:#value"></input>
<div data-elb-product="name:#innerHTML">Everyday Ruck Snack</div>
To capture a selected option from a list, use elb-ENTITY="KEY:#selected"
to
get size:20L
<select data-elb-product="size:#selected">
<option value="18L">18L</option>
<option value="20L" selected="selected">20L</option>
</select>
Arrays
To use array types, add the []
suffix to a properties name, such as
size[]:m
. The walker.js will generate de-duplicated data properties.
<div data-elb="product">
<p data-elb-product="size[]:s;size[]:l"></p>
<p data-elb-product="size[]:l"></p>
</div>
{
data: {
size: ["s", "l"],
},
// ...
}
Generic properties
Leave the entity name empty (only data-elb-
) to add the property to any
related entity. Explicitly named properties are preferred over generic ones.
It's suggested that explicit properties be preferred over generic ones.
<div data-elb-="p:v">
<div data-elb="generic">
<p data-elb-generic="k:v"></p>
<p data-elb-="g:v"></p>
<p data-elb-generic="o:v"></p>
<p data-elb-="o:x"></p>
</div>
</div>
{
data: {
p: 'v', // parent
k: 'v', // explicit
g: 'v', // generic
o: 'v' // overridden by explicit
},
// ...
}
Globals
There might be properties that don't belong to just one event but to all
events on a page. Those properties are called the globals and will be
collected once, right before the first event, got fired. The globals are
arbitrary, like the data property. What is unique about them is that you can
define them anywhere on a page using the data-elbglobals
attribute.
<div data-elbglobals="outof:scope"></div>
<div data-elb="entity" data-elb-entity="foo:bar" data-elbaction="load:action" />
This example will lead to the following event:
{
"event": "entity action",
"data": { "foo": "bar" },
"globals": { "outof": "scope" }
// other properties omitted
}
For performance reasons, the globals are only collected once per run.
Context
All entities inside a defined context want to know about their context. Those are not as relevant as globals for every event, but helpful information for every framing context of an event it is embedded in. A context could be a position or test, for example.
<div data-elbcontext="test:engagement" data-elbglobals="plan:paid">
<div data-elbcontext="recommendation:smart_ai">
<div
data-elb="promotion"
data-elbaction="click"
data-elb-promotion="title:click me"
>
click me
</div>
</div>
</div>
The context properties are tuples with the value and an index, starting at the
closest parent ([value, index]
). Access them via event.context.key[0]
.
{
event: "promotion click",
data: { title: "click me" },
globals: { plan: "paid" },
context: {
test: ["engagement", 1],
recommendation: ["smart_ai", 0],
},
// other properties omitted
}
Nested entities
A data-elb
entity within another data-elb
entity is called a nested
entity.
The walker runs through the nested entities and treats them like regular entities by gathering all related information. Nested entities are accessible in the nested array of each event. Each element is a regular entity.
<div
data-elb="mother"
data-elb-mother="label:caring"
data-elbaction="load:view"
>
<div data-elb="son" data-elb-son="age:23"></div>
<div data-elb="daughter" data-elb-daughter="age:32">
<div data-elb="baby" data-elb-baby="status:infant"></div>
</div>
</div>
This example will lead to the following event on load:
{
"event": "mother view",
"data": { "label": "caring" },
"nested": [
{ "type": "son", "data": { "age": 23 } },
{
"type": "daughter",
"data": { "age": 32 },
"nested": [{ "type": "baby", "data": { "status": "infant" } }],
},
{ "type": "baby", "data": { "status": "infant" } },
],
// other properties omitted
}
Nested entities that are nested inside another entity will be captured on both levels.
Nested entities are not available for auto-captured page view events.