Experience API

All variation.js and triggers.js files receive an object full of useful stuff.

In this article we'll show you what is contained within that object and why you might want to use it.

meta

module.exports = function variation (options) {
  console.log(options.meta)
}

The options.meta object contains a bunch of useful information about the currently executing experience and variation:

Attribute

Description

cookieDomain

The domain value that cookies have been configured to be stored against. This is useful if you need to store cookies in a consistent way

trackingId

The tracking id of the property on which the experience is executing

isPreview

Whether the variant is executing in preview mode (can be useful e.g. if you want to avoid sending data while previewing)

vertical

A code representing your industry sector. This affects what events you should send and their prefix

namespace

The value of your configured namespace if you have one. This allows you to define a custom schema

experienceId

The id of the currently executing experience

iterationId

The id of the currently executing iteration

variationId

The id of the currently executing variation

variationMasterId

The master id of the currently executing variation

variationIsControl

Whether the currently executing variation is a control variation

visitorId

The visitor id

data

module.exports = function variation (options) {
  console.log(options.data)
}

The options.data object contains input captured through the configurable content feature.

state

module.exports = function variation (options) {
  console.log(options.state)
}

The options.state methods offer a simple way to pass data between the triggers.js file and the variation.js file:

module.exports = function triggers (options) {
  options.state.set('message', 1)
  return true
}

function execution (options) {
  console.log(options.state.get('message')) // 1
}

log

module.exports = function variation (options) {
  console.log(options.log)
}

A stylish logger, useful for development of experiences. See https://github.com/QubitProducts/driftwood for more info:

module.exports = function variation (options) {
  try {
    options.log.info('hello from variation ' + options.variationMasterId)
    throw new Error('stone')
  } catch (eek) {
    options.log.error('', eek)
  }
}

getVisitorState

module.exports = function variation (options) {
  options.getVisitorState().then(function (state) {
    console.log(state)
  })
}

This method allows you to get useful information about a user, including an IP address.

The getVisitorState method returns a sync-p style promise usually resolved synchronously, but on the first view of a session, syncing with the lookup API will cause it to be asynchronous. Cross-domain setups will always have an asynchronous getVisitorState method. However, this setup is rare.

Attribute

Description

viewTs

A timestamp for the most recent view event

pageViewNumber

The number of views the visitor has had

cookiePersists

False if the user's cookie fails to persist

timezoneOffset

The timezone offset of the visitor in minutes from UTC

visitorId

Equivalent of context Id and cookie Id that is randomly assigned to each visitor to identify them on your site - device specific

conversionNumber

The number of conversions the visitor has made - updated on the next view event after a conversion

conversionCycleNumber

The number of conversion cycles the visitor has had across their lifetime.

The conversionCycleNumber increments only on the session following a session with one or more conversions

lifetimeValue

The total amount transacted by the visitor over their lifetime - updated at the beginning of the next session

firstViewTs

The date of the first View for the visitor

lastViewTs

The date of the last View for the visitor

viewNumber

The number of view event across the user's lifetime

entranceViewNumber

The number of views the visitor has seen in the entrance

sessionNumber

The number of sessions the visitor has had across their lifetime

entranceNumber

The number of entrances the visitor has had across their lifetime

ipAddress

The IP Address of the device

entranceTs

Timestamp for when the entrance was initiated

sessionViewNumber

The number of views the visitor has seen in the session

eventNumber

The sequence number of the event for the current page view

sessionTs

Timestamp for when the session was initiated

sample

A number between 0 and 99 999 based on a hash of the visitor Id

city

The city name

cityCode

The city code

country

The country name

countryCode

The ISO 4217 currency code for the user e.g. GBP, USD

latitude

The latitude value

longitude

The longitude value

area

The area name

areaCode

The area code

region

The region name

regionCode

The region code

TIP: A typical use case for getting a user's IP address is to exclude a range of IP addresses from Qubit tests.

getBrowserState

module.exports = function variation (options) {
  options.getBrowserState().then(function (state) {
    console.log(state)
  })
}

Returns useful information about the user's browser.

The getBrowserState method returns a sync-p style promise that always resolves synchronously.

Attribute

Description

url

The current URL

host

The current host

referrerUrl

The referrer URL

doNotTrack

Whether the user has expressed a preference not to be tracked by web sites

ua.deviceType

The users' device type

ua.deviceName

The users' device name

ua.osName

The users' OS name

ua.osVersion

The users' OS version

ua.browserName

The users' Browser name

ua.browserVersion

The users' Browser version

ua.userAgent

The users' User Agent string

isBot

Whether the user is a bot

time

A timestamp for when the users' browser state was computed

screenWidth

The users screen width

screenHeight

The users screen height

poll

Poll allows you to wait for:

  • The presence of DOM elements
  • The presence of window variables
  • Arbitrary conditions

and returns a promise for those elements, variables or the results of your custom poll functions:

// Wait for the presence of DOM elements by passing in a selector:
options.poll('body > .nav').then(function (nav) {
  console.log(nav)
})

// Wait for window variables to be defined:
options.poll('window.foo.bar').then(function (bar) {
  console.log(bar)
})

// Wait for arbitrary conditions to become truthy by passing in a custom function:
options.poll(() => true).then(result => console.log(result))

// Mix and match:
options.poll(['body > .nav', 'window.foo', () => 1234]).then(function ([nav, foo, id]) {
  console.log(nav, foo, id)
})

// Create a poller instance with several targets
var poller = options.poll([
  'body > .nav',
  'window.foo.bar',
  () => true
])

// Start polling
poller
  // returns a promise for the items being polled for
  .then(function ([nav, bar, truthy]) {
    console.log(nav, bar, truthy)
  })

// Stop polling
poller.stop()

// Start polling again
poller.start()

This uses @qubit/poller under the hood, but is configured to provide useful logging when in preview mode.

emitCustomGoal

const Promise = require('sync-p')

module.exports = function triggers (options) {
  const $ = require('jquery')
  let $button

  return options.poll([ '#add-to-bag' ]).then(handleButtonClick)


  function handleButtonClick ([target]) {
    return new Promise(resolve => {
      $button = $(target)
      $button.on('click', trackAddToBagClick)
      options.onRemove(() => $button.off('click', trackAddToBagClick))

      function trackAddToBagClick () {
        options.emitCustomGoal('add-to-bag:click', {
          description: 'The user clicked on the "add to bag" button' // (256 characters max)
        })
        resolve()
      }
    })
  }

}

You can track custom goals events within your experience by emitting a custom goal event.

To make these events appear in your experience results dashboard, you need to create a custom goal that corresponds to the event you are tracking:

advanced goals

INFO: We increment the count for a custom goal each time options.emitCustomGoal() is emitted. To ensure statistical accuracy for each visitor, we only count once per iteration when reporting progress against experience goals.

emitCustomMetric

module.exports = function variation (options) {
  const createWidget = require('super-widget')
  const settings = { type: 4 }
  const widget = createWidget(settings)
  widget.on('click', () => options.emitMetric('widget.clicked'))
  widget.open()
}

Metric events are similar to custom goal events except they aren't tracked relative to the control variation.

In other words, it is ideal for tracking things that only happen as a result of the experience execution, e.g. recommendation.clicked or widget.opened.

You can emit a metric event simply by calling options.emitMetric(type).

TIP: Use the convention noun.verb for specifying type, e.g. recommendation.clicked.

emitMetric also optionally accepts productId and extra arbitrary info:

module.exports = function variation (options) {
  const createWidget = require('super-widget')
  const settings = { type: 4 }
  const widget = createWidget(settings)
  widget.on('click', productId => options.emitMetric('widget.clicked', productId, settings))
  widget.open()
}

redirectTo

module.exports = function variation (options) {
  options.redirectTo('/success')
}

Sometimes an experience might want to redirect a visitor to another page.

Doing so within an experience can result in the page unloading very quickly, and this can sometimes prevent your experience from registering that it was seen, which can affect your experience stats.

Rather than delaying your experience, this utility ensures that all the appropriate events have been sent before triggering a redirect, and is the recommended way to trigger a redirect from an experience.

registerContentAreas

To register a content area on the page, experiences can use the registerContentAreas API:

module.exports = function variation (options) {
  // This tells explorer which parts of the page this experience will be interacting with
  // When this experience is active, the experience highlighter will highlight these areas
  options.registerContentAreas(['.header', '.footer'])
}

You can also dynamically register and unregister content areas while the experience is executing:

module.exports = function variation (options) {
  options.registerContentAreas(['.header', '.footer'])
  setTimeout(() => {
    options.unregisterContentAreas(['.footer'])
  }, 500)
}

When you register content areas augmented by your experiences, you will then be able to use the Explorer tool to browse your site and those content areas will be highlighted when your experience executes.

unregisterContentAreas

The unregisterContentAreas API simply unregisters content areas registered via the registerContentAreas API.

uv

Part of the clientside API is our UV api. This enables experience developers to build experiences that take advantage of our event-based protocol.

On

uv.on(type, handler, [context])

Attaches an event handler to be called when a certain event type is emitted. The handler will be passed the event data and will be bound to the context object, if one is passed. If a regex is passed, the handler will execute on events that match the regex:

uv.on('ecProduct', function (data) {
  console.log(data)
})
// => logs data when an `ecProduct` event is emitted

var subscription = uv.on(/.*/, function (data) {
  console.log(data)
})
// => logs data for all events

The on method returns a subscription object that can detach the handler using the dispose method, and can also be used to replay events currently in the event array:

subscription.dispose()
// => detaches the event handler

subscription.replay()
// => calls the handler for all events currently in uv.events

INFO: Subscriptions that have been disposed will not call the handler when replay is called.

INFO: On Single Page Applications that emit multiple view events per page, replay() only replays events since the latest view, including the latest view event. This makes accessing data layer information about the current page easier.

Once

uv.once(type, handler, [context])

Attaches an event handler that will be called once, only on the next event emitted that matches the type specified. The handler will be passed the event data and will be bound to the context object, if one is passed. Returns a subscription object which can detach the handler using the dispose method. If a regex is passed, the handler will execute on the next event to match the regex:

uv.once('ecProduct', function (data) {
  console.log(data)
})
emit('ecProduct')
// => logs data

emit('ecProduct')
// => does not log

The once method returns a subscription object, which can detach the handler using the dispose method, and can also be used to replay events currently in the event array:

subscription.dispose()
// => detaches the event handler

subscription.replay()
// => calls the handler for all events currently in uv.events

INFO: Subscriptions that have been disposed will not call the handler when replay is called.

INFO: On single-page applications that emit multiple view events per page, replay() only replays events since the latest view, including the latest view event. This makes accessing data layer information about the current page easier.

Emit

uv.emit(type, [data])

Emits an event with the type and data specified. The data should conform to the schema for the event type emitted. All events that are emitted are given a meta property with the event type:

uv.emit('ecProduct', {
  product: {
    id: '112-334-a',
    price: 6.99,
    name: '18th Birthday Balloon',
    category: ['Party Accessories', 'Birthday Parties']
  },
  color: 'red',
  stock: 6,
  eventType: 'detail'
})
// => emits an ecProduct event

The emitted event will have meta data attached:

{
  "meta": {
    "type": "ecProduct"
  },
  "product": {
    "id": "112-334-a",
    "price": 6.99,
    "name": "18th Birthday Balloon",
    "category": ["Party Accessories", "Birthday Parties"]
  },
  "color": "red",
  "stock": 6,
  "eventType": "detail"
}

Events

The events array is a cache of events emitted since the last page load. By iterating over the array, it is possible to interpret the visitor journey or the current state of the page.

getMemberships

Returns a promise that resolves to the array of Qubit segments Ids that the visitor is a member of:

module.exports = function triggers (options) {
  return options.getMemberships().then((segments) => {
    return {
      execute: segments.includes('SG-2499-a2i8sn')
    }
  })
}

INFO: This method will only work if Segments is enabled for your property.

isMemberOf

A more convenient method for checking if the visitor is in a specific segment. Returns a promise that resolves with a boolean, which is true if they are a member of the segment and false if not:

module.exports = function triggers (options) {
  return options.isMemberOf('SG-2499-a2i8sn').then((isMember) => {
    return {
      execute: isMember
    }
  })
}

INFO: This method will only work if Segments is enabled for your property.

onMembershipsChanged

This method allows you to subscribe to Qubit segment membership changes. This is useful for doing advanced triggering logic based on segments, or for syncing Qubit segments to a third party. It returns a subscription object with a dispose method that you can use in order to unsubscribe:

module.exports = function triggers (options) {
  const subscription = options.onMembershipsChanged(({ segments, changeset }) => {
    // `segments` lists the segment IDs that the user is now a member of
    console.log(segments)
    // changeset contains the segment IDs that the user joined and left
    console.log(changeset)
  })

  // It is important to dispose of your subscription when appropriate
  options.onRemove(() => subscription.dispose())
  return true
}

INFO: This method will only work if Segments is enabled for your property.

onRemove

This hook allows you to provide some cleanup methods. If smartserve restarts, these methods will be called. You should use this hook to remove any side effects.

If you implement this method, you will also be able to use the hot reloading feature of Qubit ClI.

module.exports = function triggers (options) {
  options.onRemove(cleanup)
  return true
}

module.exports = function variation (options) {
  options.onRemove(cleanup)
}

onActivation

This hook allows you to register callbacks that will fire when an experience activates, even if the visitor is allocated to the control group. This can be useful for sending analytic events:

module.exports = function triggers (options) {
  options.onActivation(trackExperience)
  return true
}

react

This object exposes methods for interacting with Qubit's React integration. Please see Integrating With React Sites for more information.

Last updated: July 2021
Did you find this article useful?