UNPKG

@technoapple/ga4

Version:

TypeScript Node.js library to support GA4 analytics.

66 lines (54 loc) 2.4 kB
import { GA4Plugin, SendFunction, OutboundFormTrackerOptions } from '../types/plugins'; import { delegate, DelegateHandle } from '../helpers/delegate'; import { parseUrl } from '../helpers/parse-url'; function defaultShouldTrack(form: HTMLFormElement, parseUrlFn: (url: string) => { hostname: string; protocol: string; href: string }): boolean { const action = form.action; if (!action) return false; const url = parseUrlFn(action); return url.hostname !== location.hostname && url.protocol.startsWith('http'); } /** * Automatically tracks form submissions to external domains. * * Sends an event when a form's `action` attribute points to a * different hostname than the current page. */ export class OutboundFormTracker implements GA4Plugin { private delegateHandle: DelegateHandle; private send: SendFunction; private eventName: string; private shouldTrackOutboundForm: NonNullable<OutboundFormTrackerOptions['shouldTrackOutboundForm']>; private hitFilter?: OutboundFormTrackerOptions['hitFilter']; constructor(send: SendFunction, options?: OutboundFormTrackerOptions) { this.send = send; this.eventName = options?.eventName ?? 'outbound_form_submit'; this.shouldTrackOutboundForm = options?.shouldTrackOutboundForm ?? defaultShouldTrack; this.hitFilter = options?.hitFilter; const formSelector = options?.formSelector ?? 'form'; this.delegateHandle = delegate( document, 'submit', formSelector, (event, element) => this.handleFormSubmit(event, element as HTMLFormElement), { composed: true, useCapture: true } ); } private handleFormSubmit(event: Event, form: HTMLFormElement): void { if (!this.shouldTrackOutboundForm(form, parseUrl)) return; const url = parseUrl(form.action); let params: Record<string, unknown> = { form_action: url.href, form_domain: url.hostname, outbound: true, }; if (this.hitFilter) { const filtered = this.hitFilter(params, form, event); if (filtered === null) return; params = filtered; } this.send(this.eventName, params); } remove(): void { this.delegateHandle.destroy(); } }