@technoapple/ga4 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/node.js.yml +31 -31
- package/.prettierignore +1 -1
- package/LICENSE +21 -21
- package/README.md +386 -48
- package/REQUIREMENTS.md +548 -0
- package/babel.config.js +5 -5
- package/build/main/ga4/ga4.d.ts +13 -0
- package/build/main/ga4/ga4.js +24 -1
- package/build/main/helpers/debounce.d.ts +5 -0
- package/build/main/helpers/debounce.js +23 -0
- package/build/main/helpers/delegate.d.ts +8 -0
- package/build/main/helpers/delegate.js +37 -0
- package/build/main/helpers/dom-ready.d.ts +1 -0
- package/build/main/helpers/dom-ready.js +13 -0
- package/build/main/helpers/parse-url.d.ts +11 -0
- package/build/main/helpers/parse-url.js +32 -0
- package/build/main/helpers/session.d.ts +4 -0
- package/build/main/helpers/session.js +50 -0
- package/build/main/index.d.ts +9 -0
- package/build/main/index.js +19 -2
- package/build/main/plugins/clean-url-tracker.d.ts +17 -0
- package/build/main/plugins/clean-url-tracker.js +105 -0
- package/build/main/plugins/event-tracker.d.ts +27 -0
- package/build/main/plugins/event-tracker.js +76 -0
- package/build/main/plugins/impression-tracker.d.ts +32 -0
- package/build/main/plugins/impression-tracker.js +202 -0
- package/build/main/plugins/index.d.ts +8 -0
- package/build/main/plugins/index.js +20 -0
- package/build/main/plugins/media-query-tracker.d.ts +20 -0
- package/build/main/plugins/media-query-tracker.js +96 -0
- package/build/main/plugins/outbound-form-tracker.d.ts +17 -0
- package/build/main/plugins/outbound-form-tracker.js +55 -0
- package/build/main/plugins/outbound-link-tracker.d.ts +19 -0
- package/build/main/plugins/outbound-link-tracker.js +63 -0
- package/build/main/plugins/page-visibility-tracker.d.ts +24 -0
- package/build/main/plugins/page-visibility-tracker.js +93 -0
- package/build/main/plugins/url-change-tracker.d.ts +20 -0
- package/build/main/plugins/url-change-tracker.js +76 -0
- package/build/main/types/plugins.d.ts +78 -0
- package/build/main/types/plugins.js +3 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/docs/examples/react.md +95 -0
- package/docs/examples/vanilla.md +65 -0
- package/docs/examples/vue.md +87 -0
- package/jest.config.ts +195 -195
- package/package.json +56 -56
- package/src/dataLayer.ts +85 -85
- package/src/ga4/ga4.ts +69 -40
- package/src/ga4/ga4option.ts +4 -4
- package/src/ga4/index.ts +4 -4
- package/src/helpers/debounce.ts +28 -0
- package/src/helpers/delegate.ts +51 -0
- package/src/helpers/dom-ready.ts +7 -0
- package/src/helpers/parse-url.ts +37 -0
- package/src/helpers/session.ts +39 -0
- package/src/index.ts +34 -7
- package/src/plugins/clean-url-tracker.ts +112 -0
- package/src/plugins/event-tracker.ts +90 -0
- package/src/plugins/impression-tracker.ts +230 -0
- package/src/plugins/index.ts +8 -0
- package/src/plugins/media-query-tracker.ts +116 -0
- package/src/plugins/outbound-form-tracker.ts +65 -0
- package/src/plugins/outbound-link-tracker.ts +72 -0
- package/src/plugins/page-visibility-tracker.ts +104 -0
- package/src/plugins/url-change-tracker.ts +84 -0
- package/src/types/dataLayer.ts +9 -9
- package/src/types/global.ts +12 -12
- package/src/types/gtag.ts +259 -259
- package/src/types/plugins.ts +98 -0
- package/src/util.ts +18 -18
- package/test/dataLayer.spec.ts +55 -55
- package/test/ga4.spec.ts +36 -36
- package/tsconfig.json +28 -28
- package/tsconfig.module.json +11 -11
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
|
2
|
-
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
|
3
|
-
|
|
4
|
-
name: Node.js CI
|
|
5
|
-
|
|
6
|
-
on:
|
|
7
|
-
push:
|
|
8
|
-
branches: [ "main" ]
|
|
9
|
-
pull_request:
|
|
10
|
-
branches: [ "main" ]
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
build:
|
|
14
|
-
|
|
15
|
-
runs-on: ubuntu-latest
|
|
16
|
-
|
|
17
|
-
strategy:
|
|
18
|
-
matrix:
|
|
19
|
-
node-version: [14.x, 16.x, 18.x]
|
|
20
|
-
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
|
21
|
-
|
|
22
|
-
steps:
|
|
23
|
-
- uses: actions/checkout@v3
|
|
24
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
25
|
-
uses: actions/setup-node@v3
|
|
26
|
-
with:
|
|
27
|
-
node-version: ${{ matrix.node-version }}
|
|
28
|
-
cache: 'npm'
|
|
29
|
-
- run: npm ci
|
|
30
|
-
- run: npm run build --if-present
|
|
31
|
-
- run: npm test
|
|
1
|
+
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
|
3
|
+
|
|
4
|
+
name: Node.js CI
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [ "main" ]
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: [ "main" ]
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
strategy:
|
|
18
|
+
matrix:
|
|
19
|
+
node-version: [14.x, 16.x, 18.x]
|
|
20
|
+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v3
|
|
24
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
25
|
+
uses: actions/setup-node@v3
|
|
26
|
+
with:
|
|
27
|
+
node-version: ${{ matrix.node-version }}
|
|
28
|
+
cache: 'npm'
|
|
29
|
+
- run: npm ci
|
|
30
|
+
- run: npm run build --if-present
|
|
31
|
+
- run: npm test
|
package/.prettierignore
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
# package.json is formatted by package managers, so we ignore it here
|
|
1
|
+
# package.json is formatted by package managers, so we ignore it here
|
|
2
2
|
package.json
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2023 keke78ui9
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 keke78ui9
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,48 +1,386 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
ga4
|
|
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
|
-
|
|
1
|
+
# @technoapple/ga4
|
|
2
|
+
|
|
3
|
+
A lightweight TypeScript GA4 helper for browser apps.
|
|
4
|
+
|
|
5
|
+
It provides:
|
|
6
|
+
- A simple GA4 wrapper (`init`, `send`, direct `gtag` access)
|
|
7
|
+
- A `dataLayer` helper (`dataLayerHelper.get`)
|
|
8
|
+
- Opt-in automatic tracking plugins (event delegation, outbound tracking, SPA page views, impression tracking, URL cleanup, media query tracking)
|
|
9
|
+
|
|
10
|
+
## Why this library
|
|
11
|
+
|
|
12
|
+
GA4 and `gtag.js` are flexible, but many teams still need reusable browser-side utilities:
|
|
13
|
+
- Keep initialization and event sending consistent
|
|
14
|
+
- Add automatic tracking without wiring listeners repeatedly
|
|
15
|
+
- Keep plugin logic tree-shakeable and removable
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @technoapple/ga4
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { ga4 } from '@technoapple/ga4';
|
|
27
|
+
|
|
28
|
+
ga4.init({ targetId: 'G-XXXXXXX' });
|
|
29
|
+
|
|
30
|
+
ga4.send('sign_up', {
|
|
31
|
+
method: 'email',
|
|
32
|
+
plan: 'pro',
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Framework Examples
|
|
37
|
+
|
|
38
|
+
Use these integration guides for copy-paste setup patterns and lifecycle notes:
|
|
39
|
+
|
|
40
|
+
- [React example](docs/examples/react.md)
|
|
41
|
+
- [Vue example](docs/examples/vue.md)
|
|
42
|
+
- [Vanilla JavaScript example](docs/examples/vanilla.md)
|
|
43
|
+
|
|
44
|
+
## API
|
|
45
|
+
|
|
46
|
+
### `ga4.init(option)`
|
|
47
|
+
|
|
48
|
+
Initializes `window.dataLayer` and `window.gtag`, then sends:
|
|
49
|
+
- `gtag('js', new Date())`
|
|
50
|
+
- `gtag('config', option.targetId)`
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
ga4.init({ targetId: 'G-XXXXXXX' });
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### `ga4.send(eventName, eventParameters)`
|
|
57
|
+
|
|
58
|
+
Sends a GA4 event through `gtag('event', ...)`.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
ga4.send('purchase', {
|
|
62
|
+
transaction_id: 'order_123',
|
|
63
|
+
value: 99,
|
|
64
|
+
currency: 'USD',
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### `ga4.gtag`
|
|
69
|
+
|
|
70
|
+
Direct access to the typed `gtag` function.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
ga4.gtag('event', 'login', { method: 'google' });
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `ga4.use(PluginClass, options?)`
|
|
77
|
+
|
|
78
|
+
Registers a plugin and returns its instance.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { ga4, OutboundLinkTracker } from '@technoapple/ga4';
|
|
82
|
+
|
|
83
|
+
const tracker = ga4.use(OutboundLinkTracker, {
|
|
84
|
+
eventName: 'outbound_link_click',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Later
|
|
88
|
+
tracker.remove();
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### `ga4.removeAll()`
|
|
92
|
+
|
|
93
|
+
Unregisters all plugins and calls each plugin's `remove()`.
|
|
94
|
+
|
|
95
|
+
### `dataLayerHelper.get(key, getLast?)`
|
|
96
|
+
|
|
97
|
+
Reads values from `window.dataLayer`.
|
|
98
|
+
|
|
99
|
+
- `getLast` omitted or `false`: returns first matching value
|
|
100
|
+
- `getLast` set to `true`: returns last matching value
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { dataLayerHelper } from '@technoapple/ga4';
|
|
104
|
+
|
|
105
|
+
const firstValue = dataLayerHelper.get('campaign');
|
|
106
|
+
const latestValue = dataLayerHelper.get('campaign', true);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Plugin Catalog
|
|
110
|
+
|
|
111
|
+
All plugins implement:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
interface GA4Plugin {
|
|
115
|
+
remove(): void;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `EventTracker`
|
|
120
|
+
|
|
121
|
+
Declarative DOM tracking via attributes (default prefix: `data-ga4-`).
|
|
122
|
+
|
|
123
|
+
Options matrix:
|
|
124
|
+
|
|
125
|
+
| Option | Type | Default | Notes |
|
|
126
|
+
|---|---|---|---|
|
|
127
|
+
| `events` | `string[]` | `['click']` | DOM events to listen to via delegation |
|
|
128
|
+
| `attributePrefix` | `string` | `'data-ga4-'` | Reads attributes like `data-ga4-on` and `data-ga4-event-name` |
|
|
129
|
+
| `hitFilter` | `(params, element, event) => Record<string, unknown> \| null` | `undefined` | Return `null` to skip sending |
|
|
130
|
+
|
|
131
|
+
Defaults:
|
|
132
|
+
- `events: ['click']`
|
|
133
|
+
- `attributePrefix: 'data-ga4-'`
|
|
134
|
+
|
|
135
|
+
Example:
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
<button
|
|
139
|
+
data-ga4-on="click"
|
|
140
|
+
data-ga4-event-name="video_play"
|
|
141
|
+
data-ga4-video-title="Summer Launch">
|
|
142
|
+
Play
|
|
143
|
+
</button>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { ga4, EventTracker } from '@technoapple/ga4';
|
|
148
|
+
|
|
149
|
+
ga4.use(EventTracker);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### `OutboundLinkTracker`
|
|
153
|
+
|
|
154
|
+
Tracks clicks on links whose hostname differs from `location.hostname`.
|
|
155
|
+
|
|
156
|
+
Options matrix:
|
|
157
|
+
|
|
158
|
+
| Option | Type | Default | Notes |
|
|
159
|
+
|---|---|---|---|
|
|
160
|
+
| `events` | `string[]` | `['click']` | Event types for delegated tracking |
|
|
161
|
+
| `linkSelector` | `string` | `'a, area'` | CSS selector for trackable links |
|
|
162
|
+
| `shouldTrackOutboundLink` | `(link, parseUrl) => boolean` | Built-in external-hostname matcher | Override outbound detection logic |
|
|
163
|
+
| `eventName` | `string` | `'outbound_link_click'` | Custom event name |
|
|
164
|
+
| `hitFilter` | `(params, element, event) => Record<string, unknown> \| null` | `undefined` | Return `null` to cancel event |
|
|
165
|
+
|
|
166
|
+
Defaults:
|
|
167
|
+
- `events: ['click']`
|
|
168
|
+
- `linkSelector: 'a, area'`
|
|
169
|
+
- `eventName: 'outbound_link_click'`
|
|
170
|
+
|
|
171
|
+
Default params:
|
|
172
|
+
- `link_url`
|
|
173
|
+
- `link_domain`
|
|
174
|
+
- `outbound: true`
|
|
175
|
+
|
|
176
|
+
### `OutboundFormTracker`
|
|
177
|
+
|
|
178
|
+
Tracks form submissions whose `form.action` points to an external hostname.
|
|
179
|
+
|
|
180
|
+
Options matrix:
|
|
181
|
+
|
|
182
|
+
| Option | Type | Default | Notes |
|
|
183
|
+
|---|---|---|---|
|
|
184
|
+
| `formSelector` | `string` | `'form'` | CSS selector for forms to observe |
|
|
185
|
+
| `shouldTrackOutboundForm` | `(form, parseUrl) => boolean` | Built-in external-hostname matcher | Override outbound detection logic |
|
|
186
|
+
| `eventName` | `string` | `'outbound_form_submit'` | Custom event name |
|
|
187
|
+
| `hitFilter` | `(params, element, event) => Record<string, unknown> \| null` | `undefined` | Return `null` to cancel event |
|
|
188
|
+
|
|
189
|
+
Defaults:
|
|
190
|
+
- `formSelector: 'form'`
|
|
191
|
+
- `eventName: 'outbound_form_submit'`
|
|
192
|
+
|
|
193
|
+
Default params:
|
|
194
|
+
- `form_action`
|
|
195
|
+
- `form_domain`
|
|
196
|
+
- `outbound: true`
|
|
197
|
+
|
|
198
|
+
### `PageVisibilityTracker`
|
|
199
|
+
|
|
200
|
+
Tracks visible/hidden durations using `document.visibilityState`.
|
|
201
|
+
|
|
202
|
+
Options matrix:
|
|
203
|
+
|
|
204
|
+
| Option | Type | Default | Notes |
|
|
205
|
+
|---|---|---|---|
|
|
206
|
+
| `sendInitialPageview` | `boolean` | `false` | Sends initial `page_view` when page is visible |
|
|
207
|
+
| `sessionTimeout` | `number` | `30` | Minutes before visible return triggers new `page_view` |
|
|
208
|
+
| `eventName` | `string` | `'page_visibility'` | Custom visibility event name |
|
|
209
|
+
| `hitFilter` | `(params) => Record<string, unknown> \| null` | `undefined` | Return `null` to skip event |
|
|
210
|
+
|
|
211
|
+
Defaults:
|
|
212
|
+
- `sendInitialPageview: false`
|
|
213
|
+
- `sessionTimeout: 30` (minutes)
|
|
214
|
+
- `eventName: 'page_visibility'`
|
|
215
|
+
|
|
216
|
+
Default params:
|
|
217
|
+
- `visibility_state`
|
|
218
|
+
- `visibility_duration`
|
|
219
|
+
- `page_path`
|
|
220
|
+
|
|
221
|
+
### `UrlChangeTracker`
|
|
222
|
+
|
|
223
|
+
Tracks SPA navigation by patching `history.pushState`, optional `replaceState`, and listening to `popstate`.
|
|
224
|
+
|
|
225
|
+
Options matrix:
|
|
226
|
+
|
|
227
|
+
| Option | Type | Default | Notes |
|
|
228
|
+
|---|---|---|---|
|
|
229
|
+
| `shouldTrackUrlChange` | `(newPath, oldPath) => boolean` | `newPath !== oldPath` | Decide when to emit `page_view` |
|
|
230
|
+
| `trackReplaceState` | `boolean` | `false` | Also patch `history.replaceState` |
|
|
231
|
+
| `hitFilter` | `(params) => Record<string, unknown> \| null` | `undefined` | Return `null` to skip event |
|
|
232
|
+
|
|
233
|
+
Defaults:
|
|
234
|
+
- `trackReplaceState: false`
|
|
235
|
+
|
|
236
|
+
Sends `page_view` with:
|
|
237
|
+
- `page_path`
|
|
238
|
+
- `page_title`
|
|
239
|
+
- `page_location`
|
|
240
|
+
|
|
241
|
+
### `ImpressionTracker`
|
|
242
|
+
|
|
243
|
+
Tracks element impressions with `IntersectionObserver` and dynamic DOM changes via `MutationObserver`.
|
|
244
|
+
|
|
245
|
+
Options matrix:
|
|
246
|
+
|
|
247
|
+
| Option | Type | Default | Notes |
|
|
248
|
+
|---|---|---|---|
|
|
249
|
+
| `elements` | `Array<string \| ImpressionElementConfig>` | `[]` | Element IDs or config objects to observe |
|
|
250
|
+
| `rootMargin` | `string` | `'0px'` | Passed to `IntersectionObserver` |
|
|
251
|
+
| `attributePrefix` | `string` | `'data-ga4-'` | Reads matching element attributes into params |
|
|
252
|
+
| `eventName` | `string` | `'element_impression'` | Custom event name |
|
|
253
|
+
| `hitFilter` | `(params, element) => Record<string, unknown> \| null` | `undefined` | Return `null` to skip event |
|
|
254
|
+
|
|
255
|
+
Defaults:
|
|
256
|
+
- `rootMargin: '0px'`
|
|
257
|
+
- `attributePrefix: 'data-ga4-'`
|
|
258
|
+
- `eventName: 'element_impression'`
|
|
259
|
+
|
|
260
|
+
Can observe with element IDs or configs:
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
import { ga4, ImpressionTracker } from '@technoapple/ga4';
|
|
264
|
+
|
|
265
|
+
ga4.use(ImpressionTracker, {
|
|
266
|
+
elements: [
|
|
267
|
+
'hero-banner',
|
|
268
|
+
{ id: 'cta-block', threshold: 0.5, trackFirstImpressionOnly: true },
|
|
269
|
+
],
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### `CleanUrlTracker`
|
|
274
|
+
|
|
275
|
+
Intercepts `gtag` calls for `config` and `page_view` payloads and normalizes:
|
|
276
|
+
- `page_location`
|
|
277
|
+
- `page_path`
|
|
278
|
+
|
|
279
|
+
Options matrix:
|
|
280
|
+
|
|
281
|
+
| Option | Type | Default | Notes |
|
|
282
|
+
|---|---|---|---|
|
|
283
|
+
| `stripQuery` | `boolean` | `false` | Remove all query params unless allowlist is set |
|
|
284
|
+
| `queryParamsAllowlist` | `string[]` | `undefined` | Keep only selected params when stripping query |
|
|
285
|
+
| `queryParamsDenylist` | `string[]` | `undefined` | Remove selected params when not stripping query |
|
|
286
|
+
| `trailingSlash` | `'add' \| 'remove'` | `undefined` | Normalize `page_path` slash behavior |
|
|
287
|
+
| `urlFilter` | `(url) => string` | `undefined` | Final custom URL transform |
|
|
288
|
+
|
|
289
|
+
Options include:
|
|
290
|
+
- `stripQuery`
|
|
291
|
+
- `queryParamsAllowlist`
|
|
292
|
+
- `queryParamsDenylist`
|
|
293
|
+
- `trailingSlash: 'add' | 'remove'`
|
|
294
|
+
- `urlFilter`
|
|
295
|
+
|
|
296
|
+
### `MediaQueryTracker`
|
|
297
|
+
|
|
298
|
+
Tracks responsive breakpoint changes using `matchMedia`.
|
|
299
|
+
|
|
300
|
+
Options matrix:
|
|
301
|
+
|
|
302
|
+
| Option | Type | Default | Notes |
|
|
303
|
+
|---|---|---|---|
|
|
304
|
+
| `definitions` | `MediaQueryDefinition[]` | `[]` | Named breakpoint sets to track |
|
|
305
|
+
| `changeTemplate` | `(oldValue, newValue) => string` | `${oldValue} => ${newValue}` | Label formatter for change payload |
|
|
306
|
+
| `changeTimeout` | `number` | `1000` | Debounce delay in ms |
|
|
307
|
+
| `eventName` | `string` | `'media_query_change'` | Custom event name |
|
|
308
|
+
| `hitFilter` | `(params) => Record<string, unknown> \| null` | `undefined` | Return `null` to skip event |
|
|
309
|
+
|
|
310
|
+
Defaults:
|
|
311
|
+
- `changeTimeout: 1000`
|
|
312
|
+
- `eventName: 'media_query_change'`
|
|
313
|
+
|
|
314
|
+
Default params:
|
|
315
|
+
- `media_query_name`
|
|
316
|
+
- `media_query_value`
|
|
317
|
+
- `media_query_change`
|
|
318
|
+
|
|
319
|
+
## Plugin Lifecycle
|
|
320
|
+
|
|
321
|
+
Use either approach:
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
const plugin = ga4.use(EventTracker);
|
|
325
|
+
plugin.remove();
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
ga4.removeAll();
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Full Example
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
import {
|
|
336
|
+
ga4,
|
|
337
|
+
EventTracker,
|
|
338
|
+
OutboundLinkTracker,
|
|
339
|
+
UrlChangeTracker,
|
|
340
|
+
CleanUrlTracker,
|
|
341
|
+
} from '@technoapple/ga4';
|
|
342
|
+
|
|
343
|
+
ga4.init({ targetId: 'G-XXXXXXX' });
|
|
344
|
+
|
|
345
|
+
ga4.use(CleanUrlTracker, {
|
|
346
|
+
stripQuery: true,
|
|
347
|
+
queryParamsAllowlist: ['utm_source', 'utm_medium', 'utm_campaign'],
|
|
348
|
+
trailingSlash: 'remove',
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
ga4.use(EventTracker, { events: ['click', 'submit'] });
|
|
352
|
+
ga4.use(OutboundLinkTracker);
|
|
353
|
+
ga4.use(UrlChangeTracker, { trackReplaceState: true });
|
|
354
|
+
|
|
355
|
+
ga4.send('app_initialized', { env: 'production' });
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## TypeScript Support
|
|
359
|
+
|
|
360
|
+
The package ships type definitions and exports plugin option types, including:
|
|
361
|
+
- `EventTrackerOptions`
|
|
362
|
+
- `OutboundLinkTrackerOptions`
|
|
363
|
+
- `OutboundFormTrackerOptions`
|
|
364
|
+
- `PageVisibilityTrackerOptions`
|
|
365
|
+
- `UrlChangeTrackerOptions`
|
|
366
|
+
- `ImpressionTrackerOptions`
|
|
367
|
+
- `CleanUrlTrackerOptions`
|
|
368
|
+
- `MediaQueryTrackerOptions`
|
|
369
|
+
|
|
370
|
+
## Development
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
npm run build
|
|
374
|
+
npm test
|
|
375
|
+
npm run test:coverage
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Notes
|
|
379
|
+
|
|
380
|
+
- Browser-focused library: APIs rely on `window`, `document`, and browser events.
|
|
381
|
+
- Call `ga4.init(...)` before sending events or registering plugins.
|
|
382
|
+
- If you register many plugins, clean them up with `remove()` or `ga4.removeAll()` to avoid duplicate listeners in long-lived apps.
|
|
383
|
+
|
|
384
|
+
## License
|
|
385
|
+
|
|
386
|
+
ISC
|