From 79372fdcabbad50f6b8389b30746dca0917642ca Mon Sep 17 00:00:00 2001 From: Mangala Ramakrishna Date: Mon, 13 Jul 2026 21:29:37 +0530 Subject: [PATCH 1/7] vita-765-added search tracking changes --- scripts/consented/instrumentation.js | 100 ++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/scripts/consented/instrumentation.js b/scripts/consented/instrumentation.js index 52938a1c..2c2aa8eb 100644 --- a/scripts/consented/instrumentation.js +++ b/scripts/consented/instrumentation.js @@ -49,6 +49,14 @@ export function isPdpPage() { return !!document.querySelector('meta[name="sku"]'); } +/** + * Search-result page detection via .search-results container. + * @returns {boolean} + */ +export function isSearchPage() { + return !!document.querySelector('.search-results'); +} + /** * Product display name for Adobe Analytics productID (Magento parity: ;{name};;;;). * @returns {string} @@ -335,7 +343,94 @@ 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 || {}; + + window.digitalData.page.pageInfo.onsiteSearchTerm = searchTerm || ''; + window.digitalData.page.pageInfo.onsiteSearchToolType = toolType; + window.digitalData.page.pageInfo.onsiteSearchResults = resultCount; + + whenSatelliteReady(() => { + configureAnalyticsTrackingServers(); + const satellite = getSatellite(); + if (toolType === 'siteSearch') { + satellite.track('searchSiteSearch'); + } else if (toolType === 'browseRecipe') { + satellite.track('searchBrowseRecipe'); + } else if (toolType === 'browseArticle') { + satellite.track('searchBrowseArticle'); + } + debugLog('Adobe Analytics search event fired', window.digitalData.page.pageInfo); + }, `search:${toolType}`); +} + +/** + * 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) { + const observer = new MutationObserver(() => { + const params = new URLSearchParams(window.location.search); + const searchTerm = params.get('search') || ''; + const toolType = getSearchToolTypeFromUrl(); + const count = parseInt(resultsCountEl.textContent, 10) || 0; + setDigitalDataForSearch(searchTerm, toolType, count); + }); + observer.observe(resultsCountEl, { childList: true, characterData: true, subtree: 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'); + 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() { @@ -343,4 +438,7 @@ export function initInstrumentation() { if (isPdpPage()) { trackProdView(); } + if (isSearchPage()) { + trackSearchResults(); + } } From 766db58ab84b243411d9e17c2e5bd23a8074e8d7 Mon Sep 17 00:00:00 2001 From: Mangala Ramakrishna Date: Wed, 15 Jul 2026 14:52:14 +0530 Subject: [PATCH 2/7] updated changes to fix psi check issues. --- scripts/consented/instrumentation.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/scripts/consented/instrumentation.js b/scripts/consented/instrumentation.js index 2c2aa8eb..70c6ce36 100644 --- a/scripts/consented/instrumentation.js +++ b/scripts/consented/instrumentation.js @@ -373,15 +373,6 @@ export function setDigitalDataForSearch(searchTerm, toolType, resultCount) { window.digitalData.page.pageInfo.onsiteSearchResults = resultCount; whenSatelliteReady(() => { - configureAnalyticsTrackingServers(); - const satellite = getSatellite(); - if (toolType === 'siteSearch') { - satellite.track('searchSiteSearch'); - } else if (toolType === 'browseRecipe') { - satellite.track('searchBrowseRecipe'); - } else if (toolType === 'browseArticle') { - satellite.track('searchBrowseArticle'); - } debugLog('Adobe Analytics search event fired', window.digitalData.page.pageInfo); }, `search:${toolType}`); } From 5a8779ccd229159c75f3cff3cab993e819d68225 Mon Sep 17 00:00:00 2001 From: Mangala Ramakrishna Date: Wed, 15 Jul 2026 17:54:22 +0530 Subject: [PATCH 3/7] updated changes to fix psi failures --- scripts/consented/instrumentation.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/consented/instrumentation.js b/scripts/consented/instrumentation.js index 70c6ce36..4654f8e6 100644 --- a/scripts/consented/instrumentation.js +++ b/scripts/consented/instrumentation.js @@ -372,9 +372,7 @@ export function setDigitalDataForSearch(searchTerm, toolType, resultCount) { window.digitalData.page.pageInfo.onsiteSearchToolType = toolType; window.digitalData.page.pageInfo.onsiteSearchResults = resultCount; - whenSatelliteReady(() => { - debugLog('Adobe Analytics search event fired', window.digitalData.page.pageInfo); - }, `search:${toolType}`); + debugLog('Adobe Analytics search data set', window.digitalData.page.pageInfo); } /** @@ -390,7 +388,7 @@ function attachSearchResultsObserver(resultsCountEl) { const count = parseInt(resultsCountEl.textContent, 10) || 0; setDigitalDataForSearch(searchTerm, toolType, count); }); - observer.observe(resultsCountEl, { childList: true, characterData: true, subtree: true }); + observer.observe(resultsCountEl, { childList: true, characterData: true }); } /** From 644e2c7d4b8ddae23898f073dfbd6dc11e6e7bb9 Mon Sep 17 00:00:00 2001 From: Mangala Ramakrishna Date: Fri, 17 Jul 2026 11:33:18 +0530 Subject: [PATCH 4/7] addressed review comments --- scripts/consented/instrumentation.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/consented/instrumentation.js b/scripts/consented/instrumentation.js index 4654f8e6..49c2d0aa 100644 --- a/scripts/consented/instrumentation.js +++ b/scripts/consented/instrumentation.js @@ -53,7 +53,7 @@ export function isPdpPage() { * Search-result page detection via .search-results container. * @returns {boolean} */ -export function isSearchPage() { +function isSearchPage() { return !!document.querySelector('.search-results'); } @@ -381,9 +381,13 @@ export function setDigitalDataForSearch(searchTerm, toolType, resultCount) { * @param {Element} resultsCountEl */ function attachSearchResultsObserver(resultsCountEl) { + let lastSearchTerm = null; const observer = new MutationObserver(() => { const params = new URLSearchParams(window.location.search); const searchTerm = params.get('search') || ''; + // 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); From bd73bee83fb47cf1d6f35cf1826bbf40cdc55d44 Mon Sep 17 00:00:00 2001 From: Mangala Ramakrishna Date: Fri, 17 Jul 2026 17:30:09 +0530 Subject: [PATCH 5/7] Added nullSearch Satellite tracking for zero results. --- scripts/consented/instrumentation.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/scripts/consented/instrumentation.js b/scripts/consented/instrumentation.js index 49c2d0aa..6f44423b 100644 --- a/scripts/consented/instrumentation.js +++ b/scripts/consented/instrumentation.js @@ -368,11 +368,18 @@ export function setDigitalDataForSearch(searchTerm, toolType, resultCount) { window.digitalData.page = window.digitalData.page || {}; window.digitalData.page.pageInfo = window.digitalData.page.pageInfo || {}; - window.digitalData.page.pageInfo.onsiteSearchTerm = searchTerm || ''; - window.digitalData.page.pageInfo.onsiteSearchToolType = toolType; - window.digitalData.page.pageInfo.onsiteSearchResults = resultCount; - - debugLog('Adobe Analytics search data set', window.digitalData.page.pageInfo); + if (resultCount === 0) { + const satellite = getSatellite(); + if (satellite?.track) { + satellite.track('nullSearch'); + debugLog('Adobe Analytics nullSearch fired', { searchTerm, toolType }); + } + } else { + window.digitalData.page.pageInfo.onsiteSearchTerm = searchTerm || ''; + window.digitalData.page.pageInfo.onsiteSearchToolType = toolType; + window.digitalData.page.pageInfo.onsiteSearchResults = resultCount; + debugLog('Adobe Analytics search data set', window.digitalData.page.pageInfo); + } } /** From 01157316c0862752a4c79ba792fdc1fc7b6aaffa Mon Sep 17 00:00:00 2001 From: Mangala Ramakrishna Date: Mon, 20 Jul 2026 15:13:37 +0530 Subject: [PATCH 6/7] fixed review comments --- scripts/consented/instrumentation.js | 66 ++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/scripts/consented/instrumentation.js b/scripts/consented/instrumentation.js index 6f44423b..3ad0470f 100644 --- a/scripts/consented/instrumentation.js +++ b/scripts/consented/instrumentation.js @@ -368,6 +368,12 @@ export function setDigitalDataForSearch(searchTerm, toolType, resultCount) { window.digitalData.page = window.digitalData.page || {}; window.digitalData.page.pageInfo = window.digitalData.page.pageInfo || {}; + // Always populate pageInfo fields first so the Launch rule reads current values, + // regardless of whether this is a null-result or normal search. + window.digitalData.page.pageInfo.onsiteSearchTerm = searchTerm || ''; + window.digitalData.page.pageInfo.onsiteSearchToolType = toolType; + window.digitalData.page.pageInfo.onsiteSearchResults = resultCount; + if (resultCount === 0) { const satellite = getSatellite(); if (satellite?.track) { @@ -375,31 +381,63 @@ export function setDigitalDataForSearch(searchTerm, toolType, resultCount) { debugLog('Adobe Analytics nullSearch fired', { searchTerm, toolType }); } } else { - window.digitalData.page.pageInfo.onsiteSearchTerm = searchTerm || ''; - window.digitalData.page.pageInfo.onsiteSearchToolType = toolType; - window.digitalData.page.pageInfo.onsiteSearchResults = resultCount; debugLog('Adobe Analytics search data set', window.digitalData.page.pageInfo); } } +/** Debounce delay (ms) for the results-count observer — prevents rapid live-search prefixes + * from each firing a separate nullSearch event before the user finishes typing. + */ +const SEARCH_DEBOUNCE_MS = 300; + +/** + * Read the current state of resultsCountEl and fire search tracking immediately. + * Called on initial attach so URL-driven searches that completed before the observer + * was registered are not missed. + * @param {Element} resultsCountEl + * @param {string} searchTerm + */ +function processCurrentSearchResult(resultsCountEl, searchTerm) { + const toolType = getSearchToolTypeFromUrl(); + const count = parseInt(resultsCountEl.textContent, 10) || 0; + setDigitalDataForSearch(searchTerm, toolType, count); +} + /** * Attach a MutationObserver to #results-count so digitalData is updated * automatically every time the search widget finishes a runSearch cycle. + * The callback is debounced so that rapid live-search mutations coalesce + * into a single tracking call once the query has settled. * @param {Element} resultsCountEl */ function attachSearchResultsObserver(resultsCountEl) { let lastSearchTerm = null; + let debounceTimer = null; + + const params = new URLSearchParams(window.location.search); + const initialSearchTerm = params.get('search') || ''; + + // Process the already-rendered result immediately so the initial URL-driven + // search is not missed (MutationObserver does not replay past mutations). + if (initialSearchTerm) { + lastSearchTerm = initialSearchTerm; + processCurrentSearchResult(resultsCountEl, initialSearchTerm); + } + const observer = new MutationObserver(() => { - const params = new URLSearchParams(window.location.search); - const searchTerm = params.get('search') || ''; - // 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); + clearTimeout(debounceTimer); + debounceTimer = setTimeout(() => { + const currentParams = new URLSearchParams(window.location.search); + const searchTerm = currentParams.get('search') || ''; + // 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); + }, SEARCH_DEBOUNCE_MS); }); - observer.observe(resultsCountEl, { childList: true, characterData: true }); + observer.observe(resultsCountEl, { childList: true, characterData: true, subtree: true }); } /** @@ -418,7 +456,9 @@ export function trackSearchResults() { return; } - // #results-count is injected dynamically by buildSearchFiltering — wait for it + // #results-count is injected dynamically by buildSearchFiltering — wait for it. + // Once found, disconnect immediately and attach the debounced search observer + // which will also process the current (already-completed) result. const containerObserver = new MutationObserver((_, obs) => { const el = container.querySelector('#results-count'); if (el) { From 4739ef0fde8a8c9c6490afaeae3bba839e8234f1 Mon Sep 17 00:00:00 2001 From: Mangala Ramakrishna Date: Mon, 20 Jul 2026 15:32:17 +0530 Subject: [PATCH 7/7] fixed result count issue when we hit the url with searchterm --- scripts/consented/instrumentation.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/consented/instrumentation.js b/scripts/consented/instrumentation.js index 3ad0470f..38e09752 100644 --- a/scripts/consented/instrumentation.js +++ b/scripts/consented/instrumentation.js @@ -419,9 +419,16 @@ function attachSearchResultsObserver(resultsCountEl) { // Process the already-rendered result immediately so the initial URL-driven // search is not missed (MutationObserver does not replay past mutations). + // Only lock in lastSearchTerm when the element already has a real count (> 0). + // If the count is still 0/empty the widget hasn't finished rendering yet — + // leaving lastSearchTerm as null lets the first observer mutation fire correctly + // instead of being silently skipped by the dedup guard. if (initialSearchTerm) { - lastSearchTerm = initialSearchTerm; - processCurrentSearchResult(resultsCountEl, initialSearchTerm); + const initialCount = parseInt(resultsCountEl.textContent, 10) || 0; + if (initialCount > 0) { + lastSearchTerm = initialSearchTerm; + processCurrentSearchResult(resultsCountEl, initialSearchTerm); + } } const observer = new MutationObserver(() => {