Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin: Element Tracking #1400

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c290839
Initial element tracking plugin
jethron Oct 24, 2024
96636e3
Further work on element tracking plugin
jethron Nov 15, 2024
e9cc70e
ShadowRoot support
jethron Nov 15, 2024
90e99e8
Update demo page/docs
jethron Nov 15, 2024
94684b4
Updates & docs
jethron Nov 22, 2024
2d055d4
Unit tests
jethron Nov 22, 2024
d9ec02c
More doc updates
jethron Nov 26, 2024
0fe6142
Fix demo page
jethron Nov 27, 2024
c7adb3d
Reset observers after previous shutdown
jethron Nov 27, 2024
f74e965
Shuffle circular imports
jethron Nov 27, 2024
9eac717
Make individual observers optional
jethron Nov 27, 2024
177ce39
Validate shadow selectors
jethron Nov 27, 2024
58a6906
Small tidy
jethron Nov 27, 2024
38b0e41
More tests
jethron Nov 27, 2024
f690d31
Tests + fix for multiple trackers
jethron Nov 28, 2024
6b8f097
Update state handling
jethron Dec 10, 2024
14b8880
Add stats tracking
jethron Dec 10, 2024
52bd2ea
Schema changes
jethron Dec 10, 2024
0cd54c5
Add page ping support to demo page
jethron Dec 10, 2024
0ab3e8c
Integrate Element Tracking plugin with repo
jethron Dec 10, 2024
cbf6ef8
Test for element stats
jethron Dec 11, 2024
0f63181
Migrate demo page to README
jethron Dec 11, 2024
57eafad
Run rush change
jethron Dec 11, 2024
3f57ff8
Fix package.json version
jethron Dec 11, 2024
ba623c3
Make tests v4 compatible
jethron Dec 12, 2024
b8cc5df
Fix subtree create/destroy detection
jethron Dec 12, 2024
ef814ed
Update element_statistics for schema feedback
jethron Jan 7, 2025
9ee8525
element: keep track of originating page view ID
jethron Jan 7, 2025
6aade84
Use previously known size for obscure/destroy events
jethron Jan 7, 2025
a758174
Fix createdTs handling
jethron Jan 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
More doc updates
jethron committed Dec 11, 2024

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
commit d9ec02c79d359dc11191f889f6ca1dddcba34628
10 changes: 8 additions & 2 deletions plugins/browser-plugin-element-tracking/README.md
Original file line number Diff line number Diff line change
@@ -30,16 +30,22 @@ npm install @snowplow/browser-plugin-element-tracking

## Usage

Initialize your tracker with the SnowplowMediaPlugin:
Initialize your tracker with the SnowplowElementTrackingPlugin and then call `startElementTracking`:

```js
import { newTracker } from '@snowplow/browser-tracker';
import { SnowplowElementTrackingPlugin } from '@snowplow/browser-plugin-element-tracking';
import { SnowplowElementTrackingPlugin, startElementTracking } from '@snowplow/browser-plugin-element-tracking';

newTracker('sp1', '{{collector_url}}', {
appId: 'my-app-id',
plugins: [ SnowplowElementTrackingPlugin() ],
});

startElementTracking({
elements: [
{selector: '.newsletter-signup'}
]
});
```

For a full API reference, you can read the plugin [documentation page](https://docs.snowplow.io/docs/collecting-data/collecting-from-own-applications/javascript-trackers/browser-tracker/browser-tracker-v3-reference/plugins/element-tracking/).
2 changes: 1 addition & 1 deletion plugins/browser-plugin-element-tracking/src/api.ts
Original file line number Diff line number Diff line change
@@ -285,7 +285,7 @@ const detailedComponentGenerator = baseComponentGenerator.bind(null, true, confi
*
* If a `cb` function is provided, it is called with the above generators as parameters that it can use in asynchronous situations (such as the JavaScript tracker).
*
* @param cb
* @param cb Callback function to receive the generator callbacks described above asynchronously.
* @returns Array of callbacks described above.
*/
export function getComponentListGenerator(
8 changes: 8 additions & 0 deletions plugins/browser-plugin-element-tracking/src/components.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,14 @@ import { getElementDetails } from './data';
import { ComponentsEntity, ElementDetailsEntity, Entities } from './schemata';
import { nodeIsElement } from './util';

/**
* Generic callback for providing `component` entities for given Elements.
* Auto discovers element and name from parameters and returns the list of components that encapsulate that element.
* @param withDetails Whether details for each component should be included.
* @param configurations List of configurations that contain component definitions.
* @param params Arbitrary parameters that should include an element and optionally a logical name for that element.
* @returns `component_parents` entity, or if `withDetails` specified, a list of entities containing `component_parents` and the details for each component as `element` entities.
*/
export const baseComponentGenerator = (
withDetails: boolean,
configurations: Configuration[],
14 changes: 7 additions & 7 deletions plugins/browser-plugin-element-tracking/src/data.ts
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ export function isDataSelector(val: unknown): val is DataSelector {
* @param element Element to select data from
* @param path The CSS path used to select `element`, which may be requested by the `selector`
* @param selectors The list of DataSelectors to evaluate
* @returns
* @returns Flattened list of results found processing the element.
*/
export function extractSelectorDetails(element: Element, path: string, selectors: DataSelector[]): AttributeList {
return selectors.reduce((attributes: AttributeList, selector) => {
@@ -238,12 +238,12 @@ export function buildContentTree(

/**
* Builds an `element` entity.
* @param config
* @param element
* @param rect
* @param position
* @param matches
* @returns
* @param config Configuration describing any additional data that should be included in the entity.
* @param element The elment this entity will describe.
* @param rect The position/dimension information of the element.
* @param position Which match this element is amongst those that match the Configuration's selector.
* @param matches The total number, including this one, of elements that matched the Configuration selector.
* @returns The Element entity SDJ.
*/
export function getElementDetails(
config: Configuration,
17 changes: 17 additions & 0 deletions plugins/browser-plugin-element-tracking/src/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Configuration } from './configuration';

/**
* Parses custom boundaries config.
* @param boundaryPixels Input format boundaries.
* @returns Object describing the input boundary configuration.
*/
export function defineBoundaries(boundaryPixels: number | [number, number] | [number, number, number, number]) {
let boundTop: number, boundRight: number, boundBottom: number, boundLeft: number;
if (typeof boundaryPixels === 'number') {
@@ -20,6 +25,13 @@ export function defineBoundaries(boundaryPixels: number | [number, number] | [nu
return { boundTop, boundRight, boundBottom, boundLeft };
}

/**
* Gets descendent elements of `target` (`document` by default) that match the selector info in `config`.
* This is usually a wrapper around `querySelectorAll`, but accounts for shadow roots that can be specified in `config`.
* @param config The configuration containing the selector and other details that influence how elements will be found.
* @param target The root to start finding descendent elements from; defaults to `document`
* @returns Array of elements within `target` that matched the configuration in `config`.
*/
export function getMatchingElements(config: Configuration, target: ParentNode = document) {
const { selector, shadowOnly, shadowSelector } = config;
const elements: Element[] = shadowOnly ? [] : Array.from(target.querySelectorAll(selector));
@@ -38,6 +50,11 @@ export function getMatchingElements(config: Configuration, target: ParentNode =
return elements;
}

/**
* Type guard to check that `Node` is an `Element`
* @param node Node to check.
* @returns If `node` is an Element.
*/
export function nodeIsElement(node: Node): node is Element {
return node.nodeType === Node.ELEMENT_NODE;
}