How to use Full Picture’s Custom Events

Home » Support » Docs » How to use Full Picture’s Custom Events

Setting up Full Picture’s Custom Events is recommended for users with at least basic understanding of HTML, CSS and Javascript.

Custom events are only available in Full Picture PRO

By default Full Picture integrates with e-commerce plugins to automatically send e-commerce data to your chosen tracking tools.

However, if you are not using any plugin (or your plugin is currently not supported) you can still send e-commerce data with Full Picture’s custom events.

FP Events are plugin agnostic, meanining that you can grab data from ANY plugin. The only requirement is having enough data. But more on this later.

How do Full Picture’s Custom Events work

The basic concept is simple.

FP’s Custom Events are special pieces of code that grab data from the HTML of your site and send it to the tracking tools you enabled in Full Picture’s settings.

To set up Full Picture’s Custom Events you can use a Javascript code or a shortcode (examples later).

Since they both work and look very similar, in this guide I will concentrate on the JS implementation and will only mention differences in the shortcodes at the end.

Basic example

This is an example of a Javascript code that is used to configure a custom event.

FP.set('single product', {
  'items' : '.main-product',
  'item id' : '{{|get:data-id}}',
  'item name' : '{{h2 span|get:text|replace:SALE:}}',
  'item category' : '{{.prod-category|get:text}}',
  'currency' : 'USD',
});

FP.track('single product', 'add to cart', 'click', 'button.add-to-cart');

Function FP.set() contains information what data should be grabbed from the HTML.

Function FP.track() contains information when this data should be grabbed and in what format it instructs Full Picture how it should reformat the data to send it to tracking tools. In this case it will be a format for “add to cart” events.

Full list of supported events is available in this document (I will tell you more about this document later, so keep it open).

FP.set() and FP.track() do not need to come one after another.

While in most cases this is how they will be placed, there may also be cases where a different placement will be required.

FP.set can, for example, be set in an external file loaded in the document head while FP.track() can be e.g. added dynamically by a script or an AJAX load.

The only thing that you actually need to remember is that FP.set() needs to come before FP.track().

FP.set()

FP.set('data object name', {data object}, 'overwrite');

FP.set takes 3 parameters:

  1. The name of the data object (string) – required. It can be any name you want.
  2. The data object – required and it cannot be empty, e.g. {}
  3. (optional) “replace” or “update” – when specified, the data object will replace or update the data object set with a previous FP.set() with the same data object’s name.

FP.track()

FP.track('data object name', 'event name', 'trigger', 'condition')

FP.track() takes 4 parameters:

  1. data object name (string) – this must be the same name as provided in the FP.set()
  2. event name (string) – this must be one of the names from the event names from this document
  3. trigger (string) – states when the object should be sent
  4. condition (string) – says when exactly to fire the event

Trigger types and available conditions:

TriggerConditionDescription
instantnoneThe data will be grabbed from HTML and sent the moment the FP.track() function is printed onto the page.
after200Similar to “instant” but the data will be grabbed from HTML and sent X milisenonds after the FP.track() function is printed onto the page.
pageloadnoneThe data will be grabbed from HTML and sent when the page finishes loading.
after pageload200 Similar to “pageload” but the data will be grabbed from HTML and sent X milisenonds after the page finishes loading.
clickbutton.add-to-cartThe data will be grabbed from HTML and sent after a specified HTML element is clicked.
form submitform#my_form_idThe data will be grabbed from HTML and sent after a specified HTML form is submitted.
in view.my-popupThe data will be grabbed from HTML and sent after a specified HTML element is visible on screen (one of its sides must be at least 200px inside the visible part of the page).

Additionally, if you add “!” in front of any trigger name, this event will not be triggered after page refresh., e.g. “! instant”, “! pageload”, etc.

Please mind, that the names of triggers need to be written in small caps

Examples

The script below can be translated to “send ‘add to cart’ event with product details when a button is clicked”

FP.track( 'product data', 'add to cart', 'click', '#product form.details .add-to-cart' );

The script below can be translated to “send a purchase event with order details after the #my-form form is submitted”.

FP.track( 'order info', 'purchase', 'form submit', '#my-form');

This event will be sent when FP.track() is added to page’s HTML, but it will not be sent if the page was previously refreshed (as indicated by “!”).

FP.track( 'product', 'product view', '! instant' );

Creating an object with data

Full Picture requires you to provide in FP.set() a non-empty object.

The names of object’s properties are specified and their full list is available in this spreadsheet.

The screenshot below comes from this spreadsheet.

(1) This is the name name of the event you need to add in FP.track()
(2) These are the properties you can use in the FP.set() data-object
(3) This list shows which properties need to be present in the data object to send the data to a given tracking tool
(4) And finally, you can also see an example use-case

Now, let’s take a look at only the data object passed to the FP.set() function.

{
  'items' : '.main-product',
  'item id' : '{{|get:data-id}}',
  'item name' : '{{h2 span|get:text|replace:SALE:}}',
  'item price' : '{{span.price|get:text|replace:$:|to:float}}',
  'item category' : '{{.prod-category|get:text}}',
  'currency' : 'USD',
  'list name' : {{|get:data-listname}},
}

As you can see, some values are typical strings, like “USD”. There can also be integers or floating numbers but there is also one special type of data value – recipies . They start and end with ‘{{ }}’ and I will explain later how they work. But before that let’s learn about some other, special properties you need to know about.

Special property: “items”

“Items” is used to track multiple elements of the same type, like all products on page, all articles, etc. Let’s take a look at an example.

Below you have a simple HTML of a cart.

<div id="cart">
  <ul>

    <li class="product" data-id="prod-a-123">
      <button type="button" class="remove">x</button>
      <p class="prod-name">Sale! Product A<strong></p>
      <span class="prod-price">$129.99</span>
    </li>


    <li class="product" data-id="prod-b-abc">
      <button type="button" class="remove">x</button>
      <p class="prod-name">Product B<strong></p>
      <span class="prod-price">$159.99</span>
    </li>

</div>

To track this data, we will need to set up this code:

FP.set('products in cart', {
  'items' : '#cart li',
  'item id' : '{{|get:data-id}}',
  'item name' : '{{.prod-name|get:text|replace:SALE! :}}',
  'item price' : '{{.prod-price|get:text|replace:$:|to:float}}',
  'currency' : 'USD',
});

FP.track('products in cart', 'checkout', '! instant');

As you can see “Items” property takes a CSS selector that points at products in cart.

If items is not indicated the only “item” will be considered a document’s <body> element or a context element.

Special property “context”

“Context” is very often used with “in view” or “click” events to limit the number of elements we want to track.

To understand how it works, let’s once again look at an example HTML of a cart, but this time, we will be tracking clicks in “remove” buttons.

FP.set('product removed from cart', {
  'context' : 'li.product',
  'item id' : '{{|get:data-id}}',
  'item name' : '{{.prod-name|get:text|replace:SALE! :}}',
  'item price' : '{{.prod-price|get:text|replace:$:|to:float}}',
  'currency' : 'USD',
});

FP.track('product removed from cart', 'remove from cart', 'click', '#cart .remove');

As you can see, in this example our tracking is triggered by a click in “remove” button.

This button is inside the <li> with class “product”. And this element also contains other product data that we want to track.

That is why we set the context to be this element.

When the event is triggered Full Picture will look for the first parent element of the “button” that matches the “context” and set it as a base from which it will grab the rest of the data.

Special properties: “unique” and “unique session”

Other special properties are “unique” and “unique session”

They are used to limit the number of times an event is sent.

  1. “unique” limits it to just a single time (forever)
  2. and “unique session” limits it to a single time in a session

In order for them to work however, they need to have a unique value.

It can be a unique number, unique string ID or a pointer to a unique value in other properties of the object, like in the example below.

FP.set('purchased products', {
  'items' : 'table.ordered-products tr'
  'item id' : '{{|get:data-id}}',
  'item name' : '{{.prod-name|get:text}}',
  'item price' : '{{.price span|get:text|replace:$ :|to:float}}',
  'currency' : 'USD',
  'order value' : '{{.total-purchase|get:text|to:float}}',
  'order id' : '{{#order-id|get:text|replace:order :}}',
  'unique' : 'order id'
});

FP.track('purchased products', 'purchase', 'instant');

As you can see the value of ‘unique‘ is set to ‘order id‘, which is another property in this object.

Recipies

Recipies are short instructions of how to grab content from the page and how to format it. They contain a step-by-step procedure on how to do it.

For example the recipe below grabs the text from the last link from the breadcrumbs.

{{#breadcrumbs a:last-of-type|get:text}}

This one on the other hand takes the “id” from the “data-id” attribute of a form and changes it from a string to an integer.

{{.product form.options|get:data-id|to:integer}}

Recipies are processed right after the event is triggered.

Parts of recipies

There are couple rules of formatting a recipe.

  1. All recipies are strings that start with “{{” and end with “}}”.
  2. Recipe “steps” are separated by a “pipe” symbol “|”.
  3. First step of the recipe contains a CSS selector that points to the element that has the data we want to grab
  4. Apart from the CSS selector all other “steps” can’t use the colon symbol (“:”) in any other way than listed below
  5. “Steps” need to follow the following order:
    1. CSS selector (optional)
    2. limit (optional)
    3. get (required)
    4. remove colons (optional)
    5. to / add before / add after / trim / replace / count / encode – all of them are optional and may be used multiple times
  6. If you are not using a CSS selector (I will explain later, situations where you don’t need it) the data will be grabbed from the item element. However you still need to use the “pipe” symbol before the begining of another step.

Here’s a full list of elements that can go into a recipe after a selector.

FunctionRequiredValueUseExample
limitnofirstget first elementlimit:first
lastget last elementlimit:last
getyesidget value of the element’s idget:id
data-[name]get value of the data attributeget:data-name
valueget value of a form fieldget:value
textget textget:text
[any attribute]get any attribute of an elementget:target or
get:rel
remove colonsnouse to remove “:” from stringremove colons
tonointegerchange value to integerget:integer
floatchange value to a number with a floating point. If a coma is used in the original price it is replaced with a full stopget:float
add beforenostringadd a string to the begining of the previous valueadd before:$
add afternostringadd a string to the end of the previous valueadd after: USD
trimnoremove whitespace from the begining and end of the stringtrim
replaceno[string]:[string]replace a string with another string (spaces and letter size are respected). It can also be used to remove parts of the text if no text to replace the original one is given.replace:Sale! :New
countnoCounts the number of elements in an array or letters in a stringcount
encodenoEncodes data before sending. Useful for personal data, like emailsencode

Example 1

Grab the product name and remove a part of its text.

'{{body.product h1.product-title|get:text|replace: - SALE!!:}}'

If the page is titled, “iPad Air 2020 – SALE!!” the result of running the above recipe would be “iPad Air 2020”. Please pay attention to spaces!

Example 2

Grab the last product price, remove the currency symbol and turn it into a number with a decimal point

'{{body.product form.details .price, body.product .final-price|limit:last|replace:PLN:|to:float}}'

This will grab string “299,99 PLN” and turn it into a number 299.99. Coma in the original price will be automatically changed into a full stop.

Example 3

Grab the value of an email field and encode it

'{{form.subscribe input[type="email"]|get:value|encode}}'

Shortcodes

As I’ve already said, you can configure FP Custom Events using either Javascript or shortcodes.

To use shortcodes however you need to enable them in Full Picture settings.

After you do that, you will be able to use 2 new shortcodes “fp_set” and “fp_track” like below.

[fp_set name="product data"]
	^product id^ : ^{{.product form.options|get:data-id}}^
	^product name^ : ^{{h1|get:text}}^
	^product category^ : ^{{#breadcrumbs a:last-of-type|get:text}}^
	^product qty^ : ^{{!.product form.options input.qty|get:value}}^
	^product price^ : ^{{!.product form.options .price|get:text|replace:$:|to:float}}^
	^currency^: ^USD^
	^ga interaction^ : true
[/fp_set]

[fp_track data="product data" event="add to cart" trigger="click" condition="button.add_to_cart"]

As you can see they look very similar to Javascript and they don’t require much explanation. The only 2 things thing you need to remember about are

  1. you have to switch all single quotes to ^ symbols.
  2. do not put comas at the end of lines

Solutions to common problems

I am getting an error “Updating failed. The response is not a valid JSON response.”

This may happen when you enter the Javascript code directly in HTML block of Gutenberg editor.

The best way around it is use a specialised plugin for adding Javascript to pages or to use Custom Event shortcodes (see above).

My scripts/shortcodes do not work!

Make sure that your code is error-free. It won’t work otherwise. Enable debug mode in Full Picture’s setting so view errors in your browser’s console.

The most common errors for scripts and shortcodes are:

  1. missing/too many comas
  2. using semi-colons instead of comas at the end of lines with object properties
  3. missing/too many quotes