Skip to content

Add page engagement tracking and outbound link click signals - #14

Open
winsmith wants to merge 6 commits into
mainfrom
claude/sweet-mccarthy-6zs0s9
Open

winsmith wants to merge 6 commits into
mainfrom
claude/sweet-mccarthy-6zs0s9

Conversation

@winsmith

Copy link
Copy Markdown
Contributor

Summary

This PR adds two new signal types to the TelemetryDeck Web SDK: page engagement metrics (scroll depth and time on page) and outbound link click tracking. The SDK now sends three types of signals: pageview (on load), TelemetryDeck.Web.pageLeave (on page exit), and TelemetryDeck.Web.linkClick (on outbound link clicks).

Key Changes

  • Page Engagement Tracking: New trackPageEngagement() module that monitors scroll depth and engaged time (time page is visible in foreground). Sends a pageLeave signal with:

    • scrollDepth: Deepest point reached (0-100%)
    • scrollDepthMilestone: Milestone reached (0, 25, 50, 75, or 100)
    • engagedSeconds: Seconds page was visible in foreground
    • Enabled by default; can be disabled with data-page-engagement="false"
  • Outbound Link Click Tracking: New trackLinkClicks() module that sends TelemetryDeck.Web.linkClick signals with:

    • TelemetryDeck.Link.url: Destination URL (credentials removed)
    • TelemetryDeck.Link.host: Destination hostname
    • TelemetryDeck.Link.isOutbound: Whether link goes to different host
    • Supports explicit tracking with data-td-link attribute
    • Supports exclusion with data-td-ignore attribute
    • Can be disabled with data-outbound-links="false"
    • Uses navigator.sendBeacon to ensure delivery during navigation
  • Code Organization: Refactored SDK into modular structure:

    • config.mjs: Centralized configuration from script attributes
    • send.mjs: Shared signal sending logic with buildBody() and send()/sendBeacon() functions
    • page-engagement.mjs: Page engagement tracking
    • outbound-links.mjs: Link click tracking
    • telemetrydeck.mjs: Main entry point that orchestrates modules
  • Documentation: Comprehensive README updates with signal descriptions, parameters, example queries, and configuration options

  • Testing: Extensive test coverage with 13 new test cases covering page engagement, link tracking, configuration options, and edge cases

  • CI/CD: Updated GitHub Actions workflows to use Node 20 and latest action versions

Notable Implementation Details

  • Page engagement uses requestAnimationFrame to coalesce scroll events for efficient layout reads
  • Link tracking uses event capture phase to catch clicks even if handlers prevent bubbling
  • Supports dynamically added links via event delegation
  • Handles both left-click and middle-click navigation
  • Respects shadow DOM with composedPath() fallback
  • Signals include same url and referrer as pageview for easy joining

https://claude.ai/code/session_01MXrtX3cjpjGaERccqmxsjv

Send a second signal per page load, `TelemetryDeck.Web.pageLeave`, the
first time the page is hidden or unloaded. It carries the maximum scroll
depth reached (percent), a 25/50/75/100 milestone bucket and the number
of seconds the page was visible, as `TelemetryDeck.PageEngagement.*`
parameters. The signal shares `url` and `referrer` with the pageview.

The signal is sent with `navigator.sendBeacon` as a plain-text body
(no CORS preflight, so it survives unload), falling back to a keepalive
fetch. Tracking is on by default and can be disabled with
`data-page-engagement="false"`.

To make room for this, the entry module is split into `config.mjs`
(script-tag attributes, test-mode detection), `send.mjs` (body builder,
fetch and beacon transports) and `page-engagement.mjs` (measurement).
The pageview request itself is unchanged.

Tests cover the new signal, the short-page case, the opt-out and that
only one signal is sent even though both visibilitychange and pagehide
fire. Also fixes the referrer fixture, which loaded a non-existent
unminified bundle, and the duplicated empty-app-id test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjanDUedGAKE6LDgN8Eo5h
All three workflows failed before running a command: setup-node@v2's
pnpm cache hit the retired cache API, upload-artifact@v3 is refused by
GitHub, and pnpm 6.19 cannot read the pnpm 8 lockfile in this repo.
Bump checkout, setup-node and upload-artifact to v4, use pnpm 8 and
Node 20 in ci.yml, deploy.yml and release.yml.

Also add a test that loads the SDK with `async`, which the README now
recommends, and format the README snippets.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjanDUedGAKE6LDgN8Eo5h
`playwright install --with-deps` for Playwright 1.36 requests apt
packages (libasound2, libffi7, libx264-163) that no longer exist on
the Ubuntu 24.04 image behind ubuntu-latest, so the Testing job failed
before running any test. Pin the job to ubuntu-22.04, which this
Playwright version targets.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjanDUedGAKE6LDgN8Eo5h
WebKit measured 49% where Chromium and Firefox measured 50% for a
scroll of exactly one viewport on a four-viewport page, because of
sub-pixel layout differences and the heading's default margin. Scroll
1.2 viewports instead (55%) and zero the fixture margins so the
milestone assertion no longer sits on a rounding edge.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjanDUedGAKE6LDgN8Eo5h
Send a `TelemetryDeck.Web.linkClick` signal whenever a visitor clicks a
link that leads to another site, carrying the destination URL, its host
and whether it is outbound as `TelemetryDeck.Link.*` parameters. The
signal goes through the existing `sendBeacon` transport to the Web SDK
endpoint, so it never delays the navigation.

Detection uses one delegated capture-phase listener for `click` and
`auxclick` (middle button) on `document`, walking `composedPath()`, so
links added after load, links inside shadow roots and clicks on nested
elements are all covered. Any element can be tracked explicitly with
`data-td-link` (the attribute value is the destination for buttons that
redirect), excluded with `data-td-ignore`, and automatic detection can
be turned off with `data-outbound-links="false"` to track only marked
elements.

Addresses #12.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MXrtX3cjpjGaERccqmxsjv
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants