ConversionRuler Coding Best Practices
When writing JavaScript code to instrument a site using there are a few best practices and issues which are a common issue to deal with:
Contents
Safe ConversionRuler Code Execution
This pattern for any code on your site:
<script> (function (w, e) { w[e] = w[e] || []; w[e].push(function (cr) { // ConversionRuler instrumentation goes here }); }(window, '_crq')); </script>
Or, less terse but more clear:
<script> window._crq = window._crq || []; window._crq.push(function (cr) { // ConversionRuler instrumentation goes here }); </script>
Should be used; largely as it has some quite beneficial side effects:
- This code is run immediately once ConversionRuler loads (use __CR.ready and other calls to defer instrumentation as needed)
- This code will not run if ConversionRuler is not loaded (due to an error on the page or any other issue) as the code is run by ConversionRuler's _crq functionality.
- Code is run in isolation from the browser state (although
window
global is always available in the browser)
Instrumentation Tips
When forms are actually loaded
With modern web marketing services, many sites use 3rd-party form collection and analytics tools such as HubSpot, Salesforce, ActOn, Gravity Forms and related tools.
3rd party tools such as these often have the following types of issues:
- Content is loaded remotely, often after the main page loads, so form elements are unavailable immediately to instrument - these tools often place iframe elements on a page which creates security issues
- Content is often loaded on a 3rd-party domain name which is inaccessible from the primary domain name due to security on the browser
The timing issues should be handled by one of two methods:
- Adding __CR.ready calls wrapped around instrumentation which requires the entire DOM loaded prior to instrumentation
- Modifying code to use a watcher pattern to wait for an element to appear
Watcher patterns
A watcher pattern can use the built-in JavaScript function window.setInterval or use the built-in execution tool for ConversionRuler __CR.watchers
The basic pattern is:
- Optionally limiting the search for specific pages where the element will appear
- Know you are looking for a specific element on the page at a certain point
- Optionally setting a timeout on the appearance of elements
Scanning the page for DOM elements can cause performance issues with the page, depending in the complexity of the layout and how things are structured.
<script> (function (w) { w._crq = w._crq || []; w._crq.push(function (cr) { var timeout = 10; // seconds cr.jquery(function ($) { var start = 1 * new Date(); cr.watchers.push(function () { var now = 1 * new Date(), selector = "button[type=submit]"; if ($(selector).length) { $("form").submit(function () { cr.track("message", null, cr.data("email")); return false; }); } if (now - start > timeout * 1000) { // Timeout after N seconds console.error("Did not find " + selector + " - timing out after " + timeout + " seconds"); return false; } }); }); }); }(window)); </script>
The above code breaks down as follows:
- Scan the page for an item of type
selector
- When found, attach a submit handler to any
form
on the page - Time out after 10 seconds and log a message
Watcher patterns can also be used to find elements which load late on a page, after a delay or other scripts generate content.
Thank You page vs. Form Submit
From a control perspective; issues with tracking forms have issues with false positives - a form submitted but not received by the server due to validation issues:
- Form submitted, but validation failed in the browser
- Form submitted, but validation failed on the server
- Form submitted, validation succeeded on the server
When a form is submitted; determining whether the form submitted successfully is often difficult due to the inability to intercept browser control prior to form submission. Issues are that some plugins and form validations (such as CAPTCHAs) do not relinquish control of the page prior to submission.
That said, it's generally recommended to instrument form submission NOT upon form submit but upon known acceptance by a server after form validation occurs. Using form submit to capture forms is acceptable in many cases but due to form validation issues it's often more robust to track upon form thank you page.
In general, form submission usually behaves in one of two manners of submission:
- The form is submitted asynchronously using JavaScript to a server, which returns a status or content to display success
- The form is submitted using HTTP to a server, which redirects the browser to another page
In both cases, the server may do one of three things as a result of the form submission:
- Return an error (form was NOT submitted)
- Redirect to another page
- Display or return success content on the existing page
For asynchronous forms which use JavaScript's XMLHttpRequest or some variant, the recommended method of tracking is:
- When redirecting to another page - Thank you page tracking
- Displays success content on the existing page - Add a watcher for element to appear on the page
For forms which submit to the server via HTTP:
- When redirecting to another page - Thank you page tracking
- When redirecting to the same page - Watcher for an element to appear
Thank you page tracking
Thank you page tracking generally matches a page path against a known pattern and records an action uniquely on the page. Issues which arise with this type of tracking are:
- Thank you pages which are shared among multiple actions
- Thank you pages which reside on a different domain name
Thank you pages which are shared among multiple actions
A thank you page which is common across multiple actions:
- Record the action code in a __CR.data variable on the form page
- Record the action using the saved code on the thank you page
On form page for "contact":
<script> (function (w) { w._crq = w._crq || []; w._crq.push(function (cr) { cr.data("thank-you-action", "contact"); }); }(window)); </script>
On form page for "newsletter":
<script> (function (w) { w._crq = w._crq || []; w._crq.push(function (cr) { cr.data("thank-you-action", "newsletter"); }); }(window)); </script>
On thank you page:
<script> (function (w) { w._crq = w._crq || []; w._crq.push(function (cr) { cr.track(cr.data("thank-you-action") || "no-action", null, cr.data("email")); }); }(window)); </script>
The thank you page will record the action based on the most recent page visited.
Thank you pages which reside on a different domain name
Tracking across domain names has many issues, and is not covered here. In most cases this scenarios occurs where the different domain name will not permit ConversionRuler to be installed.
The recommendation to handle these types of actions is to record the action on form submit on the parent domain and accept possible false positive forms.
Watcher for an element to appear
A simple script which monitors for an element to be visible on the page. It will only run on pages which begin with /contact-us/
:
<script> (function (w) { w._crq = w._crq || []; w._crq.push(function (cr) { cr.jquery(function ($) { if (cr.URL().path.indexOf("/contact-us/") === 0) { cr.watchers.push(function () { if ($('#thank-you-message:visible').length) { cr.track("contact", null, cr.data("email")); return false; } }); } }); }); }(window)); </script>
Watcher for history changes
Another issue with some modern applications is pages update the page URL using browser history, which requires monitoring the page itself for changes. A simple watcher which waits for a page to change (changes with the browser history
object):
<script> (function (w) { w._crq = w._crq || []; w._crq.push(function (cr) { cr.watchers.push(function () { if (cr.URL().path.indexOf("/thank-you-contact/") === 0) { cr.track("contact", null, cr.data("email")); return false; } }); }); }(window)); </script>