Click around to generate pixel events
This site is built in four web frameworks. Each page documents its own events.
For each web framework, how is the same site served?
| Web framework | URL | Represents |
|---|---|---|
| Traditional | /button.html | Server-rendered, multi-page sites (WordPress, Drupal, storefronts); every click loads a new document and reboots the pixel |
| SPA /{path} | /button | History-mode SPAs (modern React default). URLs are formatted like /button, with no .html extension. No such extensionless file exists, so CloudFront returns the SPA shell (spa.html), which renders the appropriate page. |
| SPA /#/{path} | /spa.html#/button | Hash-routed SPAs (Vue hash mode, React HashRouter) |
| SPA /#!/{path} | /spa.html#!/button | Hashbang SPAs (AngularJS lineage) |
Five signals make the pixel evaluate whether an effective page change occurred. Only select URL changes matter — path, query string, or a route-style hash — anchor jumps do not.
For each web framework, how do you fire that signal?
| Web framework | Full page load | hashchange event | history.pushState() | history.replaceState() | Back / forward (popstate event) |
|---|---|---|---|---|---|
| Traditional | click any link | [negative example] click an anchor link | — | — | — (browser Back arrives as another full page load) |
| SPA /{path} | arrive from outside, or refresh | — | click a nav SPA /{path} link, or a green button | green button | browser Back / Forward |
| SPA /#/{path} | arrive from outside, or refresh | click a nav SPA /#/{path} link | green button | green button | browser Back / Forward |
| SPA /#!/{path} | arrive from outside, or refresh | click a nav SPA /#!/{path} link | green button | green button | browser Back / Forward |
?page=N — the way SPAs write pagination and filter state into the query string. They appear here and at the Scroll page's 50% and 75% markers, and only work when the page is served from a SPA row:
Deanonymization is not an event type. It is a downstream service, run in conjunction with the pipeline's enrichment step, that links an anonymous visitor to a known contact. The pixel's role is only to carry the identifying inputs.
Which inputs trigger deanonymization, and where do you generate them?
| Input | Carried by | Generate it on |
|---|---|---|
| sys_id in the URL's query string | query_params, on a page_view event | Deanon (sys_id) page |
| Email address | form_submit form_values | Form + Deanon page, email forms |
| Phone number | form_submit form_values | Form + Deanon page, phone forms |
Two rules to consider when testing.
A green sandbox does not prove: pixel behavior within real framework runtimes (React/Vue internals, including SSR hydration — possible separate effort), or third-party embeds (Jotform, Typeform, etc.).
| Events | form_submit |
|---|---|
| How | Submit a form. A form which "navigates" will reload the full page. A form which "stays on page" emulates SPA behavior by suppressing navigation. |
| Notes | Submitted values arrive in the event's form_values — the email and phone variants are distinct deanonymization inputs. |
Click a name to fill every form below.
| Name | Phone | ||
|---|---|---|---|
| Aaron Austin | aaron.austin@example.com | +1 (555) 714-0549 | |
| Baba Behiko | baba.behiko@example.com | +1 (555) 622-1254 | |
| Caba Cowoz | caba.cowoz@example.com | +1 (555) 313-3130 | |
| Daba Dedabaji | daba.dedabaji@example.com | +1 (555) 454-9676 | |
| Eduardo Murphy | eduardo.murphy@example.com | +1 (555) 474-7691 | |
| Faba Famih | faba.famih@example.com | +1 (555) 970-9060 | |
| Gaba Gepigozi | gaba.gepigozi@example.com | +1 (555) 702-8686 | |
| Haba Hojubuta | haba.hojubuta@example.com | +1 (555) 779-8809 | |
| Isaiah White | isaiah.white@example.com | +1 (555) 418-7963 | |
| Jaba Janojo | jaba.janojo@example.com | +1 (555) 981-0627 | |
| Kaba Kagojaye | kaba.kagojaye@example.com | +1 (555) 534-4146 | |
| Laba Losaw | laba.losaw@example.com | +1 (555) 905-6252 | |
| Maba Muwopageli | maba.muwopageli@example.com | +1 (555) 342-5772 |
| Events | button_click |
|---|---|
| How | Click a button. Each has a distinct button_id. |
| Events | link_click |
|---|---|
| How | Click an external link (opens a new tab). |
| Notes | Links open new tabs so this page survives repeated clicks. Same-tab link clicks (event delivery during page unload) are exercised by every nav link. |
| Events | file_download |
|---|---|
| How | Click a file link; each extension is a distinct file_type. |
zip), file name extracted as Apples| Events | video (Vimeo, YouTube, Self-hosted) |
|---|---|
| How | Play, pause, or finish any player. |
| Notes | What's under test is when a player appears. The first three are present in the page HTML — the pixel's boot-time scan finds them in the Traditional web framework, and they arrive with the page body when an SPA mounts it. The Inject buttons create players on demand, long after load, the way consent-gated and click-to-load embeds do. |
| Events | scroll (depth 25 / 50 / 75 / 90) |
|---|---|
| How | Scroll down. |
| Notes | Thresholds reset on every page change — a full load in Traditional, any effective page change in the SPAs. To verify SPAs at depth, follow the instructions on the 50% and 75% markers. |
You've crossed 50%. If you want to test resetting scroll depth in a SPA: (i) Click for an effective page change (ii) Keep scrolling (iii) Verify the scroll depth fires against the new event_id and new page.
Same test as 50% marker, but validates the pixel's handling of a different browser API.
The pixel caps scroll depth here.
| Events | page_view carrying a sys_id query param (drives deanonymization) |
|---|---|
| How | Click an identity link. |
| Notes | Each group carries the sys_id where that framework naturally puts it; the headings say where. #!/ routes carry it identically to #/. |
| Events | page_view (every link below is a navigation); its query_params reflects the URL. |
|---|---|
| How | Click a link and read query_params on the resulting page_view (debug console / network tab). |
| Notes | The pixel merges the real query string (before the #) with the query string inside an SPA hash route (#/route?…). On a key collision the hash-route value wins. Anchor hashes (#section) contribute nothing. Traditional and SPA /{path} links carry params in the real query string; #/ and #!/ links carry them inside the hash; merge links carry them in both. |
| Events | search |
|---|---|
| How | Arrive on any URL whose path contains “search”. The query params ride along in the event. |
| Notes | The pixel checks the path where the framework keeps it. /search.html and /search match on the URL's real path; /spa.html#/search matches on the route inside the hash — even though the document's real path (/spa.html) contains no “search”. The search box below is an ordinary GET form, so submitting it also emits a form_submit. |
TenonTag — Sandbox Frontend