This library provides the ability to process messages passed onto a dataLayer queue.
npm install data-layer-helper- Background
- Why Do We Need a Library?
- The Abstract Data Model
- Overwriting Existing Values
- Recursively Merging Values
- Meta Commands
- Native Methods
- Custom Methods
- Listening for Messages
- Processing the Past
- Summary
- Build and Test
- License
``html`
Page authors can append messages onto the queue in order to emit information about the page and
its state.
`html`
These messages are simply JavaScript objects containing a hierarchy of key/value pairs. They can
be metadata about the page content, information about the visitor, or data about events happening
on the page. This system allows tools like analytics libraries and tag management systems to access
this data in a standard way, so page authors can avoid using a bunch of proprietary, repetitive APIs.
* It provides a common, well defined system for exposing page data.
* It doesn't slow down page rendering.
* It doesn't pollute the global JavaScript namespace.
* It doesn't require page authors to learn a different, one-off API for every new tool.
* It doesn't require page authors to expose the same data multiple times.
* It allows page authors to add, remove or change vendors easily.
This project provides the ability to listen for dataLayer messages and to read the key/value pairs
that have been set by all the previous messages. It can be used by the tools/vendors mentioned above,
or by page authors that need to read back the data they've emitted.
To use this library, you'll need to get it onto the page. You can do this by hosting a copy and
sourcing it from the page, or by compiling it into your own JavaScript library. Once it's on the
page, you can create a new helper object like this:
`js`
var helper = new DataLayerHelper(dataLayer);
This helper object will listen for new messages on the given dataLayer. Each new message will be
merged into the helper's "abstract data model". This internal model object holds the most recent
value for all keys which have been set on messages processed by the helper.
You can retrieve values from the data model by using the helper's get() method:
`js`
helper.get('category'); // Returns "Science".
As mentioned above, messages passed onto the dataLayer can be hierarchical. For example, a page
author might push the following message, which has data multiple levels deep:
`js`
dataLayer.push({
one: {
two: {
three: 4
}
}
});
Using the helper, you can retrieve the nested value using dot-notation:
`js`
helper.get('one.two.three'); // Returns 4.
helper.get('one.two'); // Returns {three: 4}.The Abstract Data Model
As mentioned above, the abstract data model is an internal representation, which hold
the most recent value for all keys that have been set by a dataLayer message. This
means that as each message is pushed onto the dataLayer, the abstract data model must
be updated. The helper library does this using a well-defined process.
As each message is processed, its key/value pairs will be added to the abstract data
model. If the key doesn't currently exist in the model, this operation is simple.
The pair is simply added to the model object. But in the case of key conflicts, we have
to specify how values will be overwritten and/or merged.
There are two possible actions to take when merging a key/value pair onto the abstract
model; overwriting the existing value or recursively merging the new value onto the
existing value. The action taken will depend on the type of the two values. For this,
we define three types of values:
* JavaScript Arrays
* "Plain" Objects
* Everything else
Hopefully, JavaScript Arrays are self-explanatory. "Plain" Objects are JavaScript
objects that were created via Object literal notation (e.g. {one: 2}) or via "new Object".
Nulls, Dates, RegExps, Windows, DOM Elements, etc. are not "Plain". Those fall into the
category of "everything else", along with strings, numbers, booleans, undefined, etc.
Once the type of the new and existing values has been categorized this way, we can use the
following table to describe what action will happen for that key/value pair:
Existing Value | New Value | Merging Action
---------------|--------------|--------------------------
Array | Array | Recursively merge
Array | Plain Object | Overwrite existing value
Array | Other | Overwrite existing value
Plain Object | Array | Overwrite existing value
Plain Object | Plain Object | Recursively merge
Plain Object | Other | Overwrite existing value
Other | Array | Overwrite existing value
Other | Plain Object | Overwrite existing value
Other | Other | Overwrite existing value
Existing Value | New Value | Result of Overwrite
-----------------|------------------|---------------------
[1, 2, 3] | 'hello' | 'hello'
{ducks: 'quack'} | [1, 2, 3] | [1, 2, 3]
{ducks: 'quack'} | 'hello' | 'hello'
'hello' | [1, 2, 3] | [1, 2, 3]
'hello' | {ducks: 'quack'} | {ducks: 'quack'}
'hello' | 42 | 42
Existing Value | New Value | Result of Overwrite
-------------------|-------------------------------|----------------------------
{one: 1, three: 3} | {two: 2} | {one: 1, three: 3, two: 2}
{one: 1, three: 3} | {three: 4} | {one: 1, three: 4}
{one: {two: 3}} | {one: {four: 5}} | {one: {two: 3, four: 5}}
{one: {two: 3}} | {two: 4} | {one: {two: 3}, two: 4}
[] | ['hello'] | ['hello']
[1] | [undefined, 2] | [1, 2]
[1, {two: 3}] | [undefined, {two: 4, six: 8}] | [1, {two: 4, six: 8}]
The first of these syntaxes allows you to call any method supported on the existing type.
For example, if the existing value in the abstract model is an Array, you'd have a wide
variety of APIs that can be called (e.g. push, pop, concat, shift, unshift, etc.). To invoke
this syntax, you would push a "command array" onto the dataLayer instead of a normal message
object.
`js`
dataLayer.push(['abc.push', 4, 5, 6]);
A command array is a normal JavaScript array, where the first element is a string. The string
contains the key of the value to update, followed by a dot (.), followed by the name of the
method to invoke on the value. In the above example, the key to update is 'abc', and the
method to invoke is the 'push' method. The string may be followed by zero or more arguments,
which will be passed to the invoked method.
If the given method name does not exist on the existing value, or if the invocation throws an
exception, the assignment will be ignored, and a warning message will be logged to the browser's
developer console (if available).
| Existing Key: | abc |
| Existing Value: | [1, 2, 3] |
| Command Array: | dataLayer.push(['abc.push', 4, 5, 6]) |
| Result: | [1, 2, 3, 4, 5, 6] |
In the following example, no arguments are provided:
| Existing Key: | abc |
| Existing Value: | [1, 2, 3] |
| Command Array: | dataLayer.push(['abc.pop']) |
| Result: | [1, 2] |
In the following example, the value to update (bbb) is nested inside a top level object (aaa):
| Existing Key: | aaa.bbb |
| Existing Value: | [1, 2, 3] |
| Command Array: | dataLayer.push(['aaa.bbb.push', 4]) |
| Result: | [1, 2, 3, 4] |
And the following example demonstrates an operation on a Date object. Remember that all types
are supported, not just Arrays:
| Existing Key: | time |
| Existing Value: | Fri Dec 20 2013 15:23:22 GMT-0800 (PST) |
| Command Array: | dataLayer.push(['time.setYear', 2014]) |
| Result: | Fri Dec 20 2014 15:23:22 GMT-0800 (PST) |
Notice that because command arrays are processed asynchronously, nothing can be done with the
return values from these method invocations. This brings us to our second syntax for updating
values in the abstract data model.
When a function is processed, it will be executed in the context of the abstract data model. The
value of "this" will be an interface that represents the current abstract data model. This
interfact will provide two APIs: get(key) and set(key, value). The following examples demonstrate
how these APIs can be used to update values in the abstract data model.
| Existing Key: | time |
| Existing Value: | Fri Dec 20 2013 15:23:22 GMT-0800 (PST) |
| Custom function: | dataLayer.push(function() { |
| Result: | Fri Jan 20 2013 15:23:22 GMT-0800 (PST) |
The following example demonstrates updating a nested value:
| Existing Key: | aaa.bbb.ccc |
| Existing Value: | [1, 2, 3] |
| Custom function: | dataLayer.push(function() { |
| Result: | [1, 2, 6] |
The following example demonstrates overwriting a value:
| Existing Key: | abc |
| Existing Value: | [1, 2, 3] |
| Custom function: | dataLayer.push(function() { |
| Result: | {xyz: [1, 2, 3]} |
`js`
function listener(message, model) {
// Message has been pushed.
// The helper has merged it onto the model.
// Now use the message and the updated model to do something.
}
var helper = new DataLayerHelper(dataLayer, listener);
`js`
function listener(message, model) {
// Message has been pushed.
// The helper has merged it onto the model.
// Now use the message and the updated model to do something.
}
var helper = new DataLayerHelper(dataLayer, listener, true);
Using this option means that your listener callback will be called once for every message that
has ever been pushed onto the given dataLayer. And on each call to the callback, the model
will represent the abstract model at the time of the message.
There are three types of messages:
* Standard Messages (Objects)
* Native Method Calls (Command Arrays)
* Custom Method Calls (Functions)
This helper library provides tools and vendors a way to consume these messages. It automatically
listens for new messages and merges them onto its abstract data model. You can query the model
using the get() API, or you can get message notifications with a callback function.
At this point, we highly recommend that you read the code and browse the tests for examples of
how the library works and how it can be used.
A few prerequisites:
1. Install Node.js and npm
2. Install Git
Clone a copy of the project repo by running:
`bash`
git clone --recursive git://github.com/google/data-layer-helper.git
Install the grunt-cli package if you haven't before. This should be done as global install:
`bash`
npm install -g grunt-cli
Enter the data-layer-helper directory and install the Node dependencies, this time without specifying a global install:
`bash`
cd data-layer-helper
npm install
Enter the third_party/closure-linter directory and install the Closure linter:
`bash`
cd third_party/closure-linter
python setup.py install
Make sure you have grunt installed. From the root directory of the project, run:
`bash`
grunt -version
That should be everything. You can try running the build, which will run the linter, compile/minify the JavaScript and run the tests.
`bash`
grunt
The built version (data-layer-helper.js) will be in the dist/` subdirectory.
Copyright 2012 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.