> ## Documentation Index
> Fetch the complete documentation index at: https://docs.abtestly.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Activation triggers

> Fire the exposure when it matters, on load, on click, when an element scrolls into view, or when your own code says so.

By default an experiment fires as soon as the page has matched Location and
Audience rules. That is fine for most tests. It is wrong for a few.

An **activation trigger** shifts the moment of exposure. Instead of the
page load itself, the trigger decides when the visitor counts as
"in the test" and when the variant's JavaScript runs. That timing lands in
your funnel numbers, so it matters.

## The four modes

| Mode                  | Fires when                                              | Common use                                                  |
| --------------------- | ------------------------------------------------------- | ----------------------------------------------------------- |
| **On load** (default) | The page has matched targeting rules                    | Most page-level tests                                       |
| **On click**          | A visitor clicks a CSS selector                         | Testing pieces of a page the user has to reach for          |
| **When visible**      | An element becomes at least 1 % visible in the viewport | Below-the-fold tests                                        |
| **Manual**            | Your code calls `abtestly.activate("your-key")`         | Tests inside a modal, a wizard, or code you already control |

## Why not just fire on load

Because a visitor who never reached your CTA is not part of a CTA test.
Counting them dilutes both arms equally, but it also shows a much lower
"conversion rate" than the number your funnel already shows, and stakeholders
notice.

Two examples:

**A hero-CTA experiment on a long landing page.** If it fires on load, the
visitor counts even if they bounced from the fold. Switch to **When
visible** on the CTA's own element, and the visitor only counts when the
CTA scrolled into view.

**An "upgrade" modal that shows on a specific click.** If it fires on load,
every page visitor is in the test, but only a small fraction ever see the
modal. Switch to **On click** on the trigger button, or **Manual** if you
open the modal from your own code.

## On click

Pick a CSS selector. Any click that matches, via
`event.target.closest(selector)`, activates the experiment.

* ABTestly installs a single passive capturing click listener per site and
  dispatches to the right selectors from there. It does not fight your
  handlers.
* Both arms, control and variant, activate on the click. That is how you
  keep the two sides comparable.
* If nothing on the page matches your selector when the page loads, ABTestly
  logs a debug event so you can catch selector typos in preview.

## When visible

Pick a CSS selector. When an element matching that selector is at least
1 % on-screen, the experiment activates.

* Uses one `IntersectionObserver` per experiment. It is a browser API,
  cheap.
* Fires **once** per visitor per experiment per pageview. A visitor who
  scrolls past the element and back does not re-activate.

## Manual

Sometimes the moment you want to count is inside code you already write.
step three of a wizard, the point after a form's validation succeeds, the
opening of a modal.

Call:

```js theme={null}
abtestly.activate("your-experiment-key");
```

Return values (for `dev.apply` diagnosis, not typical use):

| Return value              | What happened                                                  |
| ------------------------- | -------------------------------------------------------------- |
| `"activated"`             | The visitor entered the experiment and the exposure fired      |
| `"already_active"`        | The visitor is already in this experiment, ignored             |
| `"unknown_experiment"`    | The key is not one of your running experiments                 |
| `"not_eligible"`          | This visitor did not match targeting rules                     |
| `"not_bucketed"`          | Targeting matched but they did not enter (allocation \< 100 %) |
| `"wrong_activation_mode"` | The experiment is not set to Manual                            |

The **key** is the experiment's short code (`ABTESTLY_EXP_KEY`), not its UUID
or its display name. You can find it on the experiment detail page next to
the name.

## SPA behaviour

On a single-page app route change, ABTestly clears the current route's
pending activation registrations and re-arms triggers for the eligible
experiments on the new route. In practical terms: your click and visibility
triggers behave the same on route two as they did on route one, without you
doing anything special.

## Exclusion groups and activation

An experiment in an [exclusion group](/targeting/exclusion-groups) cannot
also use an activation trigger, the editor disables the field. Exclusion
group members are decided at page load; deferring the decision to a click
or a manual call would break the guarantee that at most one member ever
matches a given visitor.

## The screen

<Frame caption="Activation section inside the experiment editor.">
  <img src="https://mintcdn.com/abtestly/TJFfWsOMn4uzK6iy/images/experiment-editor.png?fit=max&auto=format&n=TJFfWsOMn4uzK6iy&q=85&s=2e533fee8b3cabc0ea9b545e2054c098" alt="Activation section with mode picker" width="2400" height="1141" data-path="images/experiment-editor.png" />
</Frame>
