-
Notifications
You must be signed in to change notification settings - Fork 229
Expand file tree
/
Copy pathapi.ts
More file actions
126 lines (114 loc) · 4.41 KB
/
api.ts
File metadata and controls
126 lines (114 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { BrowserPlugin, BrowserTracker, dispatchToTrackersInCollection } from '@snowplow/browser-tracker-core';
import { DynamicContext, CommonEventProperties, resolveDynamicContext } from '@snowplow/tracker-core';
import { createEventFromButton, filterFunctionFromFilter } from './util';
import { buildButtonClick } from './core';
import { ButtonClickEvent, ButtonClickTrackingConfiguration, FilterFunction } from './types';
const _trackers: Record<string, BrowserTracker> = {};
// The event listeners added to the document, for each tracker
// This allows them to be removed with `removeEventListener` if button click tracking is disabled
const _listeners: Record<string, (event: MouseEvent) => void> = {};
/**
* Button click tracking
*
* Will automatically tracking button clicks once enabled with 'enableButtonClickTracking'
* or you can manually track button clicks with 'trackButtonClick'
*/
export function ButtonClickTrackingPlugin(): BrowserPlugin {
return {
activateBrowserPlugin: (tracker: BrowserTracker) => {
_trackers[tracker.id] = tracker;
},
};
}
/**
* Manually log a click
*
* @param event - The event information
* @param trackers - The tracker identifiers which the event will be sent to
*/
export function trackButtonClick(
event: ButtonClickEvent & CommonEventProperties,
trackers: Array<string> = Object.keys(_trackers)
) {
dispatchToTrackersInCollection(trackers, _trackers, (t) => {
t.core.track(buildButtonClick(event), event.context, event.timestamp);
});
}
/**
* Enable automatic click tracking for all `<button>` and `<input type="button">` elements on the page
*
* @param configuration - The configuration for automatic button click tracking
* @param trackers - The tracker identifiers which the event will be sent to
*/
export function enableButtonClickTracking(
configuration: ButtonClickTrackingConfiguration = {},
trackers: Array<string> = Object.keys(_trackers)
) {
// Ensure that click tracking uses the latest configuration
// In the case of `enableButtonClickTracking` being called multiple times in a row
disableButtonClickTracking(trackers);
trackers.forEach((trackerId) => {
// Store the configuration for this tracker, if it doesn't already exist
// This allows us to enable click tracking for a tracker if it has been disabled
_listeners[trackerId] = (event: MouseEvent) => {
eventHandler(
event,
trackerId,
filterFunctionFromFilter(configuration.filter),
configuration.defaultLabel,
configuration.context
);
};
const addClickListener = () => {
document.addEventListener('click', _listeners[trackerId], true);
};
if (_trackers[trackerId]?.sharedState.hasLoaded) {
// the load event has already fired, add the click listeners now
addClickListener();
} else {
// defer until page has loaded
_trackers[trackerId]?.sharedState.registeredOnLoadHandlers.push(addClickListener);
}
});
}
/**
* Disable automatic click tracking for all `<button>` and `<input type="button">` elements on the page
*
* Can be re-enabled with {@link enableButtonClickTracking}
*/
export function disableButtonClickTracking(trackers: Array<string> = Object.keys(_trackers)) {
trackers.forEach((trackerId) => {
if (_listeners[trackerId]) {
document.removeEventListener('click', _listeners[trackerId], true);
}
});
}
/**
* Handle a click event
*
* @param event - The click event
* @param trackerId - The tracker identifier which the event will be sent to
* @param filter - The filter function to use for button click tracking
* @param context - The dynamic context which will be evaluated for each button click event
*/
function eventHandler(
event: MouseEvent,
trackerId: string,
filter: FilterFunction,
defaultLabel?: string | ((element: HTMLElement) => string),
context?: DynamicContext
) {
let elem = (event.composed ? event.composedPath()[0] : event.target) as HTMLElement | null;
while (elem) {
if (elem instanceof HTMLButtonElement || (elem instanceof HTMLInputElement && elem.type === 'button')) {
if (filter(elem)) {
const buttonClickEvent = createEventFromButton(elem, defaultLabel);
buttonClickEvent.context = resolveDynamicContext(context, buttonClickEvent, elem);
trackButtonClick(buttonClickEvent, [trackerId]);
}
// presume nested buttons aren't a thing
return;
}
elem = elem.parentElement;
}
}