findClosestElement β GTM Variable Template for GTM
findClosestElement EXTENDED GTM
Traverses up the DOM from a starting element to find the closest parent where an attribute matches a value. Returns the dataLayer path to the matching element, or undefined if not found.
When to Use This
Section titled βWhen to Use ThisβExamples
Section titled βExamplesβFind parent by tag name
INPUT
Starting Element Path: gtm.element
Attribute to Match: tagName
Value to Match: A
Comparison Mode: eql
Attribute to Match: tagName
Value to Match: A
Comparison Mode: eql
OUTPUT
gtm.element.parentElement.parentElement
No match returns undefined
INPUT
Starting Element Path: gtm.element
Value to Match: A
Attribute to Match: tagName
Comparison Mode: eql
Value to Match: A
Attribute to Match: tagName
Comparison Mode: eql
OUTPUT
undefined
Related Variables
Section titled βRelated VariablesβSame category: GTM
Under the Hood
Section titled βUnder the Hoodβπ View Implementation Code
/** * Finds the closest parent element where an attribute matches a value. * * @param {string} data.src - The starting element path in dataLayer (default: "gtm.element"). * @param {string} data.attr - The attribute to check (href, class, id, data-*, tagName, innerText, custom). * @param {string} [data.customAttr] - Custom attribute name when attr is "custom". * @param {*} data.val - Value to match against. * @param {string} [data.mod] - Comparison mode: "eql" (exact, default), "cnt" (contains), "rgx" (regex). * @param {Function} [data.fn] - Optional custom comparison function (receives attrValue, refValue). * @param {Function|string} [data.out] - Optional output handler. * * Direct-mode specific parameters: * @param {Function} [data.pre] - Optional pre-processor function. * * @returns {string|undefined} The dataLayer path to the matching element, or undefined if not found. * * @framework ggLowCodeGTMKit */const copyFromDataLayer = require('copyFromDataLayer');const getAttributePath = function(attr) { if (attr.indexOf('data-') === 0) { const camelCase = attr.substring(5).split('-').map(function(text, index) { if (index === 0) return text; return text.charAt(0).toUpperCase() + text.substring(1); }).join(''); return '.dataset.' + camelCase; } if (attr === 'tagName') return '.tagName'; if (attr === 'innerText') return '.innerText'; return '.attributes.' + attr + '.value';};const getCompareFunction = function(mode) { if (mode === 'cnt') { return function(attrValue, refValue) { return typeof attrValue === 'string' && attrValue.indexOf(refValue) > -1; }; } if (mode === 'rgx') { return function(attrValue, refValue) { return typeof attrValue === 'string' && !!attrValue.match(refValue); }; } return function(attrValue, refValue) { return attrValue === refValue; };};const findClosestElement = function(startPath, attribute, value, mode, compareFn) { if (!attribute) return undefined;
const attrPath = getAttributePath(attribute); let currentPath = startPath || 'gtm.element'; const MAX_DEPTH = 15; let depth = 0;
const compare = typeof compareFn === 'function' ? compareFn : getCompareFunction(mode);
while (depth < MAX_DEPTH && copyFromDataLayer(currentPath + '.tagName')) { const attrValue = copyFromDataLayer(currentPath + attrPath);
if (compare(attrValue, value)) { return currentPath; }
currentPath = currentPath + '.parentElement'; depth++; }
return undefined;};const safeFunction = fn => typeof fn === 'function' ? fn : x => x;const out = safeFunction(data.out);// ===============================================================================// findClosestElement - Direct mode// ===================================================================π§ͺ View Test Scenarios (5 tests)
β
'[example] Find parent by tag name'β
Test find parent by data attributeβ
Test find parent with custom contains functionβ
Test find parent with exists function (no value needed)β
'[example] No match returns undefined'