Goals

Stats3x21 min readPage 4 of 7
On this page 16

A goal is a visitor action the site exists for: a request, a call, an order, a subscription, a downloaded price list. Stats3x marks the visits in which a goal was reached and shows how many conversions there were, what share of all visits they make (conversion rate) and how much they brought (value). Goals show which sources, pages and devices lead to results, not just to visits.

How goals are counted

  1. You create a goal on the Goals tab with the Add goal button and choose its type: page visit, click on an element, form submission or visitor action (a _st3x.e() call from the page code).
  2. The page script (the Stats3x snippet) sends page views and visitor actions. It receives the CSS selectors of click and form goals by itself - there is no need to add them to the stats3x.click_selectors setting.
  3. Once a minute cron parses the incoming data and checks every page view and every action against all enabled goals. When the condition matches, a conversion is saved: visit, visitor, page, time and value.
  4. Conversions are shown on the Goals tab, in the Summary, in the visitor card, in the manager dashboard widget and in the email report.

Important: goals are not counted retroactively. Only data that cron parsed after the goal was created or changed is taken into account, so create goals in advance. If cron is not running (there is a warning on the State tab and above the reports), there will be no conversions at all. A disabled goal stops counting new conversions, old ones are kept. A deleted goal is removed together with its conversions.

Goal fields

  • Name - how the goal is called in reports, for example "Callback request".
  • Goal type - what is counted. When you change the type, the form shows the condition fields of that type.
  • Condition - resource ID or part of the address, CSS selector, category and action. See the sections about each type below.
  • Value - what one conversion is worth, in any units (dollars, euros, points). Can be left empty. This is the price of one conversion, not a total: on the goals table it is shown in small print below the sum in the Total value column, and the sum for the period is 0 while there are no conversions.
  • Count no more than once per visit - on by default: ten clicks on the phone number in one visit give one conversion. Turn it off if every repetition matters, for example every order.
  • Goal is enabled - a disabled goal is kept but does not count new conversions.

Buttons in a goal row: magnifier - all reports only for visits with this goal (the filter appears above the reports and is removed there) and opens its conversions right away, pencil - edit, Disable / Enable, trash - delete together with conversions. Creating and editing goals requires the stats3x_save permission, deleting - stats3x_remove.

Type "Page visit"

The goal is reached when a visitor opens a certain page. Good for pages that are only reached after a result: "Thank you for your request", "Order placed", "Registration complete". Fill in one of the two conditions or both - a match of either is enough.

  • Resource ID - the resource number in the MODX tree (in brackets next to the title). The most reliable way: it does not depend on the page address.
  • Or the address contains - a part of the page address. It is searched in the full address including the domain and parameters, case-insensitively. The part after # and parameters with secrets are not part of the address (see stats3x.hidden_params).
  • This is a substring search: /thanks also matches /thanks-old/, and go/ matches any address containing "go/", for example /catalog/flamingo/. Make the address part as precise as possible, with slashes on both sides: /go/.
  • The goal counts only when the page opens in the browser and the Stats3x snippet runs on it. A redirect address (for example /go/... to another site), a file or a page without the snippet cannot be tracked this way - use a Click on an element goal on the link instead, for example a[href^="/go/"].
ConditionWhat is counted
15Resource ID field: resource 15 is opened, for example the "Thank you" page
/thanks/Any page whose address contains /thanks/
/cart/The visitor reached the cart
msorder=miniShop2 order placed page (address like ?msorder=12)
utm_campaign=autumnA page with the autumn ad campaign tag in its address was opened
shop.example.comA view of any page on the shop.example.com subdomain

Type "Click on an element"

The goal is reached when a visitor clicks an element matching a CSS selector. A click on something inside the element (an icon, the button text) counts too. The middle mouse button (open in a new tab) counts, the right one does not. Good for phone, email and messenger links, Buy, Request a call, Show phone buttons.

  • The selector is the same as in CSS or document.querySelector(): .class, #id, [attribute] and their combinations. Up to 64 characters. How to write it - see How to write a CSS selector below.
  • Several selectors separated by commas - one goal for all these elements.
  • The selector matches all such elements on all pages. If every button on the site has the .btn class, the goal will count every click. Use an id or your own attribute (see the tip below).
  • A click is counted immediately, whatever happens next: a click on Send in a form with errors is still a click. For requests, a "Thank you" page visit or a visitor action from the success handler is more reliable.
  • tel: and mailto: links, downloads and outbound links go to the Actions report even without a goal. A goal is needed to count them as conversions: with conversion rate, value and visit filtering.
ConditionWhat is counted
a[href^="tel:"]A click on any phone number on the site
a[href^="mailto:"]A click on an email address
a[href*="wa.me"], a[href*="t.me"]Going to WhatsApp or Telegram
#callbackThe element with id="callback", for example the Request a call button
.btn-buyAny element with the btn-buy class, for example Buy buttons in product cards
a[href$=".pdf"]A link to any PDF file
[data-goal="phone"]An element with the data-goal="phone" attribute (see the tip below)

Tip: do not tie goals to styling classes - they change with the design. Add your own data-goal attribute in the template or chunk and use it in the goal, then the goal survives any layout rework.

<a href="tel:+15550000000" data-goal="phone">+1 555 000-0000</a>
<button type="button" data-goal="callback">Request a call</button>

Selectors for these goals: [data-goal="phone"] and [data-goal="callback"].

How to write a CSS selector

How to type it. Enter the selector into the field as is - the way it is written in the table below. Square brackets are required: [data-autopick] is an element with the data-autopick attribute, while data-autopick without brackets would mean a <data-autopick> tag, which is not on the page, and the goal would not work. A dot means a class, a hash means an id, quotes go only around a value inside the brackets. Do not type backticks or the code highlighting of this documentation into the field.

SelectorWhat it finds
#open-filtersThe element with id="open-filters"
.autopick-btnAn element with the autopick-btn class. If an element has several classes separated by spaces (class="btn btn-sm autopick-btn"), any of them matches
[data-autopick]An element with the data-autopick attribute, the value does not matter
[data-goal="phone"]An element whose data-goal attribute equals phone
a[href^="/go/"]A link whose address starts with /go/ (^= means "starts with")
a[href*="from=profile"]A link whose address contains from=profile (*= means "contains")
a[href$=".pdf"]A link whose address ends with .pdf ($= means "ends with")
button.autopick-btnTag and class without a space: only a <button> with the autopick-btn class
.catalog-toolbar [data-autopick]A space means "inside": an element with data-autopick inside a block with the catalog-toolbar class
#callback, #orderA comma means "any of": an element with the id callback or the id order

How to find a selector in the page code

  1. Open the site page, right-click the button or link → Inspect. Its HTML opens.
  2. Choose what to hook on, in order of reliability: your own data-... attribute (it is added for scripts and rarely changes) → id → link address (href) → a class that tells what the element is for (autopick-btn). Styling classes - btn, btn-sm, btn-accent - are on many buttons of the site, so the goal would count extra clicks.
  3. If there are several such elements on the site but you only need one place, put the block that contains it in front, separated by a space: .catalog-toolbar [data-autopick].
  4. Check the selector in the browser console (see How to test a goal below): document.querySelectorAll('selector').length shows how many elements it finds on the open page. 0 means the selector finds nothing.

Example 1: a button

<div class="catalog-toolbar">
    <button class="btn btn-sm autopick-btn" type="button" data-autopick>
        Find a specialist automatically
    </button>
</div>

Selector: [data-autopick]. Other buttons have the btn and btn-sm classes too, but only this one has the data-autopick attribute. If the attribute is used somewhere else as well and you only need the button in the toolbar: .catalog-toolbar [data-autopick].

<div class="profile-actions">
    <a href="/go/ivanov/?from=profile" class="btn btn-accent">Message the specialist</a>
    <a href="#similar" class="btn btn-outline">Similar specialists</a>
</div>

You need to count clicks on all /go/... links, whatever name follows. Selector: a[href^="/go/"] - links whose address starts with /go/; Similar specialists (#similar) does not match. Only links from this block: .profile-actions a[href^="/go/"], only with the from=profile tag: a[href*="from=profile"]. The brackets check the address as it is written in the href attribute: if somewhere a link is written in full (https://site.com/go/...), ^="/go/" will not find it - use *="/go/" then.

Type "Form submission"

The goal is reached when the browser submits a form: the visitor pressed the submit button or Enter in a field (the submit event). If required fields (the required attribute) are empty, the browser does not submit the form and the goal is not counted.

  • The selector must match the <form> tag itself, not the block around it. If the id or class is only on a wrapper, give the path to the form: #callback-modal form.
  • An empty selector means submission of any form on the site, including search, login and subscription. It is usually better to specify a certain form.
  • The goal counts a submission attempt, not an accepted request: errors found by the server (captcha, invalid phone) are invisible to it. If only accepted requests matter, use the "Thank you" page or a visitor action from the success handler (FetchIt and SendIt examples are in the section below).
  • Forms that a script sends without the submit event (for example, a button with a click handler and a fetch request) are not seen by this type - mark them with _st3x.e().
  • In the Actions report a submission is shown with the form name: its id, name or submit address.
ConditionWhat is counted
#order-formThe form with id="order-form"
#callback-modal formA form inside the popup with id="callback-modal"
.callback-formAll forms with the callback-form class
form[action*="subscribe"]A form submitted to an address containing subscribe
emptyAny form on the site

Type "Visitor action": _st3x.e()

The most flexible type: the goal is reached when the page code reports an event itself. Use it for things clicks and addresses cannot show: a successful AJAX form response, a placed order with its amount, a video start, an article read to the end. The call:

_st3x.e("category", "action", "label", value);
  • category - required, up to 64 characters. It is compared with the goal Action category field exactly, case-sensitively: order and Order are different categories.
  • action - up to 128 characters. If the goal Action field is empty, any action of this category matches, otherwise only an exact match.
  • label - optional, up to 255 characters: order number, form id, video address. It is not part of the goal condition and is shown in the Actions report.
  • value - an optional number. If it is passed, the conversion value is taken from it instead of the goal Value field. This is how the real order amount gets into the goal.

This is JavaScript. The line above is a sample of one call, not ready-made code. On the site it goes into a <script> tag in a template or chunk, or into a JS file of the site - without <script> it is simply printed on the page as text. The _st3x object is created by the script that the Stats3x snippet includes, so your <script> must be below the snippet call:

    ...
    [[!Stats3x]]
    <script>
    _st3x.e("order", "created");
    </script>
</body>

If the call is above the snippet (for example, in <head> or in a JS file included in <head>) and runs right on page load, the browser console shows "_st3x is not defined" and the goal does not work. In that case put the stub from Setup → step 1 at the beginning of <head>: the calls are queued and sent after the Stats3x script loads. If _st3x.e() is inside a handler of an event that happens after the page has loaded (successful form submission, video start - as in the examples below), the position of the code does not matter. Call _st3x.e() only on pages where the Stats3x snippet is output.

Examples

Each example is complete code, together with the <script> tag: paste it into a template or chunk below the Stats3x snippet. The goal in the manager is the Visitor action type with the same category (and action, if given) as in the code.

Request via FetchIt (an extra for sending forms via AJAX on MODX 3) - only a successful submission counts: the fetchit:success event fires when the server has accepted the form. Goal: category lead, action - empty for all forms or the id of one form, for example callback-form.

<script>
document.addEventListener("fetchit:success", function (e) {
    _st3x.e("lead", e.detail.form.id);
});
</script>

Request via SendIt (an extra for sending forms via AJAX, an analogue of AjaxForm that works on MODX 3) - the si:send:success event fires only on a successful server response. The FORM tag check keeps form submissions only: SendIt dispatches the same events for other requests too (pagination, file uploads). Same goal: category lead, action - empty or the form id.

<script>
document.addEventListener("si:send:success", function (e) {
    var form = e.detail.target;
    if (form && form.tagName === "FORM") {
        _st3x.e("lead", form.id);
    }
});
</script>

Order with amount. The code goes on the "Order placed" page; instead of A-1024 and 2500 output the order number and amount with your shop placeholders. Goal: category order, action created, empty Value field - the amount comes from the code. Keep "once per visit" if there is usually one order per visit: then a page reload does not count the order twice.

<script>
_st3x.e("order", "created", "A-1024", 2500);
</script>

Video start - each video once per page view. Goal: category video, action play.

<script>
document.querySelectorAll("video").forEach(function (video) {
    video.addEventListener("play", function () {
        _st3x.e("video", "play", video.currentSrc);
    }, {once: true});
});
</script>

Article read - the .article-end element at the end of the article appeared on screen (put it in the template after the text). Goal: category article, action read.

<script>
new IntersectionObserver(function (entries, observer) {
    if (entries[0].isIntersecting) {
        _st3x.e("article", "read");
        observer.disconnect();
    }
}).observe(document.querySelector(".article-end"));
</script>

Value, repetitions and conversion rate

  • Value of one conversion - the number from the Value field or the value from _st3x.e(). Stats3x does not know the currency: count in dollars, euros or points, just the same way in all goals. Example: if every fifth request becomes a 10,000 order, a request is worth 2,000.
  • The Total value column of the goals table is the sum of values of all conversions in the selected period: 3 conversions of 12 give 36. No conversions - 0, even if the goal has a value. In small print below the sum - the value of one conversion from the goal settings.
  • Once per visit on - one conversion per visit, however many times the visitor repeats the action. Off - every repetition counts: 3 orders in a visit give 3 conversions and the sum of three orders.
  • Conversions - how many times the goal was reached. Conversion rate - the share of visits with the goal among all visits in the period: 1000 visits, the goal was reached in 25 of them - 2.5 %. Repetitions within a visit do not raise the rate.
  • All numbers depend on the period and the filter above the reports: with a filter by source you see the conversion rate of that source only.

Where to see results

  • Goals - all goals with conditions, conversions, conversion rate and value for the period.
  • Conversions (a subtab of the Goals tab) - every conversion in its own row: when, which goal, on which page, where the visitor came from, device, city, IP, site user if logged in. A row opens the visitor card, the ▶ button opens the visit recording. This is where you see who exactly reached the goal.
  • Visits (the Visitors tab) - the Goals column with badges of goals reached in the visit.
  • Magnifier in a goal row opens its conversions and leaves all tabs only for visits with this goal: where those who sent a request came from, which devices, cities and pages.
  • Summary - the goals table next to top pages and sources.
  • Visitor card (a visit row on Visitors → Visits) - a green badge with the goal name at the page view where it was reached.
  • Widget on the manager dashboard - top five goals.
  • Email report - the goals table (the goals_html placeholder).

Ready recipes

Common goals and how to set them up. The code for visitor actions is in the Visitor action section above.

TaskGoal typeConditionValue and repetitions
A request followed by a "Thank you" pagePage visitResource ID of the "Thank you" pageOnce per visit
Request via an AJAX form (FetchIt, SendIt)Visitor actionleadOnce per visit; value - average revenue per request
Call from the siteClick on an elementa[href^="tel:"]Once per visit
Wrote to a messengerClick on an elementa[href*="wa.me"], a[href*="t.me"]Once per visit
EmailClick on an elementa[href^="mailto:"]Once per visit
Downloaded the price list (file address contains price)Click on an elementa[href*="price"]Once per visit
Newsletter subscriptionForm submission#subscribe-formOnce per visit
Reached the cartPage visit/cart/Once per visit
Shop order with amountVisitor actionorder / createdEmpty value - the amount comes from the code
Watched a videoVisitor actionvideo / playOnce per visit

Funnel: create a goal for each step - Reached the cart, Order - and compare their conversion rates. The difference shows how many visitors abandon checkout, and the magnifier of each goal shows where those who finished came from.

How to test a goal

  1. Create the goal and make sure its Status column says Enabled.
  2. Open the site in an incognito window or another browser. If your IP is in stats3x.exclude_ips, its data is ignored - test from a phone over mobile internet.
  3. Perform the action: open the page, click, submit the form.
  4. Wait 1-2 minutes: cron parses data once a minute. To avoid waiting, run parsing manually:
    php /path/to/site/core/components/stats3x/cron.php -v --task=parse
  5. Open the Goals tab with the Today period - the goal will have a conversion. If it does not, open the Actions tab: if the click or submission is there but there is no conversion, the goal condition did not match what arrived (compare the selector or category). If the action is not there either, the script did not send it, see "If a goal is not counted".

Selectors are easy to check in the browser console (F12 → Console) on a site page. The first command must return a number greater than zero, the second - FORM, the third - function. The fourth sends a test action: after parsing it counts for a goal with the lead category.

document.querySelectorAll('a[href^="tel:"]').length
document.querySelector("#order-form").tagName
typeof _st3x.e
_st3x.e("lead", "test")

If a goal is not counted

  • Cron is not running: the State tab shows when the data parsing task last ran. Without cron there are no conversions and no other statistics.
  • The goal was created after the action: goals are not counted retroactively.
  • The form selector points to a wrapper, not to <form>.
  • The selector does not find the element: a typo, or the element is inside an iframe (chat widgets, maps, forms from other sites) - the page script does not see clicks inside an iframe.
  • The category in _st3x.e() differs from the goal category in letter case or an extra space.
  • The Stats3x snippet is not on the page, or _st3x.e() is called before the script loads without the stub - the browser console shows "_st3x is not defined".
  • The form is sent by a script without the submit event - use _st3x.e().
  • Your IP is in stats3x.exclude_ips or the browser is controlled by a program (automated tests) - such visits are ignored.
  • The goal counts too often: the selector matches extra elements (a common class), the address part is too short, or "once per visit" is off.