Skip to content
Open
Changes from all commits
Commits
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
100 changes: 99 additions & 1 deletion scripts/consented/instrumentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ export function isPdpPage() {
return !!document.querySelector('meta[name="sku"]');
}

/**
* Search-result page detection via .search-results container.
* @returns {boolean}
*/
function isSearchPage() {
return !!document.querySelector('.search-results');
}

/**
* Product display name for Adobe Analytics productID (Magento parity: ;{name};;;;).
* @returns {string}
Expand Down Expand Up @@ -335,12 +343,102 @@ export function trackProdView(attempt = 0) {
}

/**
* Initialize Adobe Analytics instrumentation (prodView on PDP).
* Derive onsiteSearchToolType from the current URL ?type= param.
* Matches AEM logic: recipe → browseRecipe, article → browseArticle, else siteSearch.
* @returns {string}
*/
function getSearchToolTypeFromUrl() {
const params = new URLSearchParams(window.location.search);
const type = (params.get('type') || '').toLowerCase();
if (type === 'recipe') return 'browseRecipe';
if (type === 'article') return 'browseArticle';
return 'siteSearch';
}

/**
* Set digitalData search properties and fire the matching Launch direct-call rule.
* Mirrors AEM setDigitalDataForSearch(). Called after every search run in the
* search-results widget so onsiteSearchTerm/Results/ToolType are always populated.
* @param {string} searchTerm - The search string entered by the user
* @param {string} toolType - 'siteSearch' | 'browseRecipe' | 'browseArticle'
* @param {number} resultCount - Total number of results returned
*/
export function setDigitalDataForSearch(searchTerm, toolType, resultCount) {
window.digitalData = window.digitalData || {};
window.digitalData.page = window.digitalData.page || {};
window.digitalData.page.pageInfo = window.digitalData.page.pageInfo || {};

if (resultCount === 0) {
const satellite = getSatellite();
if (satellite?.track) {
satellite.track('nullSearch');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set the search fields on digitalData.page.pageInfo before calling satellite.track('nullSearch') otherwise the rule sees missing or stale term and result values.

debugLog('Adobe Analytics nullSearch fired', { searchTerm, toolType });
}
} else {
window.digitalData.page.pageInfo.onsiteSearchTerm = searchTerm || '';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you confirm the lauch has a data element changed rule watching these digitalData fields? This branch does not call satellite.track(), so a direct-call configuration would not send a successful-search event.. if change detection is intentional, the JSDoc should say that instead.

window.digitalData.page.pageInfo.onsiteSearchToolType = toolType;
window.digitalData.page.pageInfo.onsiteSearchResults = resultCount;
debugLog('Adobe Analytics search data set', window.digitalData.page.pageInfo);
}
}

/**
* Attach a MutationObserver to #results-count so digitalData is updated
* automatically every time the search widget finishes a runSearch cycle.
* @param {Element} resultsCountEl
*/
function attachSearchResultsObserver(resultsCountEl) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This observer fires on every #results-count text change, which includes pagination clicks and type-filter toggles, not just new searches. All three go through runSearch which writes to #results-count.

Is that what you want @awasthiruchi?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dylandepass confirmed with the legacy AEM code and live site's Network tab that setDigitalDataForSearch() only fires after genuine search results come back, not on pagination/filter changes.
@ramuvaram Will scope attachSearchResultsObserver to only fire on an actual new search term, so it matches current production behavior. Thanks!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated code to fire only on new search term.

let lastSearchTerm = null;
const observer = new MutationObserver(() => {
const params = new URLSearchParams(window.location.search);
const searchTerm = params.get('search') || '';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This runs for every live-search prefix, so typing one zero-result term can fire nullSearch several times. Debounce the tracking path and emit once for the final settled query.

// Only fire on an actual new search term, not pagination/filter changes
if (searchTerm === lastSearchTerm) return;
lastSearchTerm = searchTerm;
const toolType = getSearchToolTypeFromUrl();
const count = parseInt(resultsCountEl.textContent, 10) || 0;
setDigitalDataForSearch(searchTerm, toolType, count);
});
observer.observe(resultsCountEl, { childList: true, characterData: true });
}

/**
* Initialize search analytics tracking on search-result pages.
* Observes #results-count — written by the search widget after every runSearch —
* so no changes to the widget itself are needed.
* Falls back to a MutationObserver on the container if the widget hasn't rendered yet.
*/
export function trackSearchResults() {
const container = document.querySelector('.search-results');
if (!container) return;

const resultsCountEl = container.querySelector('#results-count');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MutationObserver does not replay the count update that happened before attachment, so the initial URL-driven search is missed. I confirmed this by loading:

https://search-tracking-digitaldata--vitamix--aemsites.aem.network/us/en_us/search-result?search=blender

The page rendered 163 results, but digitalData.page.pageInfo was missing onsiteSearchTerm, onsiteSearchResults, and onsiteSearchToolType. Please process the current completed result when attaching, or subscribe to an explicit search-complete event from the widget.

if (resultsCountEl) {
attachSearchResultsObserver(resultsCountEl);
return;
}

// #results-count is injected dynamically by buildSearchFiltering — wait for it
const containerObserver = new MutationObserver((_, obs) => {
const el = container.querySelector('#results-count');
if (el) {
obs.disconnect();
attachSearchResultsObserver(el);
}
});
containerObserver.observe(container, { childList: true, subtree: true });
}

/**
* Initialize Adobe Analytics instrumentation (prodView on PDP, search tracking on search pages).
* @returns {void}
*/
export function initInstrumentation() {
debugLog('Adobe Analytics instrumentation loaded');
if (isPdpPage()) {
trackProdView();
}
if (isSearchPage()) {
trackSearchResults();
}
}
Loading