Help Center / Tracking modules
04 · Tracking modules
Measr ships nine tracking modules: page views, interactions, navigation, exit links, scroll depth, errors, and downloads auto-detect after one Measr.init() call; video and forms take one line of code each. All nine are included in the single €19/month tier. This page shows what fires on its own, how to control it with data attributes, and how to wire the two manual modules.
| Module | What it tracks | Event type | Mode |
|---|---|---|---|
| A · Pages | Page views, sessions, bounce rate, entry and exit pages | page_view | Auto |
| B · Interactions | CTA clicks, toggles, accordions, tabs | interaction | Auto |
| C · Navigation | Internal nav clicks, tagged by surface (header, footer, breadcrumb) | navigation | Auto |
| D · Exit Links | Outbound link clicks with URL capture | exit_link | Auto |
| E · Scroll Depth | 25 / 50 / 75 / 90 / 100% milestones, once per page view | scroll_depth | Auto |
| F · Video | Play, milestones, completion, watch time | video | Manual |
| G · Errors | Client-side JavaScript errors, sanitized before insert | error | Auto |
| H · Downloads | File download clicks (.pdf, .zip, .csv, and more) | download | Auto |
| K · Forms | Form view, start, and submit, per registered form | form_view / form_start / form_end | Opt-in per form |
Every auto module can be switched off in the init() config, and every module also has a manual API for AJAX flows, custom widgets, and React state machines. The full signatures live in docs/MODULES.md.
document.title (brand suffixes like | Brand are stripped). On single-page apps, the SDK hooks the History API and fires again on each route change.<button> elements, <details> accordions, [role="button"], and [role="tab"]. Elements inside <nav> or <form> are excluded because modules C and K own those.<nav>, role="navigation", common nav class names, and anchors inside <header> or <footer>. Each click is classified by surface: main navigation, footer, or the container's aria-label. Every navigation click also fires a companion interaction event, so nav engagement counts in the interaction metrics.scrollMilestones: [10, 50, 90] in the init config.window.onerror and unhandled promise rejections. Messages are sanitized before insert: emails, tokens, and long digit runs become [REDACTED]..pdf, .csv, .xlsx, .zip, .mp4, and a dozen more. Override the list with downloadExtensions.| Attribute | Effect |
|---|---|
data-measr-ignore | Skip this element (and its subtree) entirely, across all modules. |
data-measr-name="..." | Override the interaction name the SDK would derive from the element's text. |
data-measr-interaction | Force-track an element the heuristics would miss; pair with data-measr-element to set the element label. |
data-measr-download-name="..." | Report a friendly file name instead of the raw path. |
<a href="/files/q4-2026.pdf" data-measr-download-name="Q4 2026 Annual Report">
Download (PDF)
</a>
<a href="/files/internal.pdf" data-measr-ignore>Don't track this one</a>
Every player has its own events and quirks, so video stays manual. One method covers them all:
Measr.trackVideo(videoName, milestone, videoSeconds);
// milestone: '0%', '25%', '50%', '75%', '90%', '100%'
// videoSeconds: seconds watched since the previous milestone
Ready-to-paste reference implementations for HTML5 <video>, the YouTube iframe API, and Vimeo Player.js are in docs/MODULES.md. In the dashboard, a video counts as completed at the 90% milestone.
Register each form you care about; the SDK then watches its full lifecycle:
Measr.trackForm('#waitlist-form', { name: 'Waitlist' });
| Event | Fires when |
|---|---|
form_view | The form enters the viewport (at least 50% visible) |
form_start | The visitor focuses the first input |
form_end | The form is submitted |
For multi-step wizards and AJAX-only flows, call the three manual variants yourself: trackFormView(name), trackFormStart(name), trackFormEnd(name). The dashboard turns these three events into a view → start → submit funnel per form.
Zero configuration. When a visitor lands with UTM parameters in the URL, the first page view of the session stores the full query string. The dashboard breaks down utm_source, utm_medium, and utm_campaign as three tables in the Traffic & Acquisition section.
https://yoursite.com/?utm_source=linkedin&utm_medium=social&utm_campaign=product-launch
Links you already tagged for GA4 work unchanged; the UTM standard is universal. Keep values lowercase and consistent (utm_source=google, never Google), otherwise you get duplicate rows in the breakdown tables. Visitors without UTM parameters are attributed by referrer, with no referrer counting as Direct.
Before any module fires, init() runs a three-layer bot check: the navigator.webdriver flag (Playwright, Puppeteer, Selenium), a user-agent list (Googlebot, Bingbot, GPTBot, ClaudeBot, and 20+ others plus catch-all tokens), and missing browser APIs. A detected bot produces zero rows. To analyze bot traffic instead, pass excludeBots: false.
Measr.enableDebug(); // logs every event payload to the console
Measr.disableDebug();
The flag persists in localStorage across reloads. One caveat that matters when verifying: the console line prints before the HTTP request, so a log entry alone does not prove delivery. The troubleshooting page shows how to confirm rows actually land.
data-measr-ignore to it or any ancestor. The SDK skips it across all modules.