🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
6.3 KiB
6.3 KiB
DataLayer Validation Reference
DataLayer Structure Basics
Proper Initialization
// Must appear BEFORE GTM script
<script>
window.dataLayer = window.dataLayer || [];
</script>
<!-- GTM script here -->
Push Syntax
// Correct
dataLayer.push({ event: "page_view", page_title: "Home" });
// Wrong - direct assignment
dataLayer = [{ event: "page_view" }]; // ❌ Overwrites array
Validation Rules
Event Names
| Rule | Valid | Invalid |
|---|---|---|
| Alphanumeric + underscore | add_to_cart |
add-to-cart |
| Max 40 characters | purchase |
(too long names) |
| Case sensitive | addToCart ≠ addtocart |
- |
| No spaces | form_submit |
form submit |
| No special chars | click_cta |
click@cta |
Parameter Names
| Rule | Valid | Invalid |
|---|---|---|
| Max 40 characters | item_category |
(too long) |
| Alphanumeric + underscore | user_id |
user-id |
Cannot start with _ |
custom_param |
_private |
| Cannot start with number | step_1 |
1_step |
Data Types
| Parameter | Expected Type | Example |
|---|---|---|
| value | number | 29.99 not "29.99" |
| currency | string (ISO 4217) | "USD", "KRW" |
| transaction_id | string | "T_12345" |
| quantity | integer | 2 not 2.0 |
| price | number | 45000 |
| items | array | [{...}, {...}] |
Type Validation Code
function validateDataLayerPush(data) {
const issues = [];
// Check value is number
if (data.ecommerce?.value !== undefined) {
if (typeof data.ecommerce.value !== 'number') {
issues.push(`value should be number, got ${typeof data.ecommerce.value}`);
}
}
// Check currency format
if (data.ecommerce?.currency) {
if (!/^[A-Z]{3}$/.test(data.ecommerce.currency)) {
issues.push(`currency should be 3-letter ISO code`);
}
}
// Check items array
if (data.ecommerce?.items) {
if (!Array.isArray(data.ecommerce.items)) {
issues.push(`items should be array`);
} else {
data.ecommerce.items.forEach((item, i) => {
if (!item.item_id) issues.push(`items[${i}] missing item_id`);
if (!item.item_name) issues.push(`items[${i}] missing item_name`);
if (item.price && typeof item.price !== 'number') {
issues.push(`items[${i}].price should be number`);
}
if (item.quantity && !Number.isInteger(item.quantity)) {
issues.push(`items[${i}].quantity should be integer`);
}
});
}
}
return issues;
}
E-commerce Object Clearing
Why Clear?
GA4 may merge previous ecommerce data with new events.
Correct Pattern
// Clear first
dataLayer.push({ ecommerce: null });
// Then push new event
dataLayer.push({
event: "view_item",
ecommerce: { ... }
});
Validation Check
function checkEcommerceClear(dataLayerArray) {
let lastHadEcommerce = false;
const issues = [];
dataLayerArray.forEach((item, i) => {
const hasEcommerce = 'ecommerce' in item;
const isNull = item.ecommerce === null;
if (hasEcommerce && !isNull && lastHadEcommerce) {
issues.push({
index: i,
message: 'Missing ecommerce:null before this push'
});
}
lastHadEcommerce = hasEcommerce && !isNull;
});
return issues;
}
Event Sequence Validation
Expected Sequences
E-commerce Purchase Flow:
view_item_list? → view_item → add_to_cart → view_cart →
begin_checkout → add_shipping_info → add_payment_info → purchase
Form Submission:
form_start → form_submit → generate_lead?
User Authentication:
login | sign_up
Sequence Validator
function validateSequence(events, expectedOrder) {
const eventNames = events
.filter(e => e.event)
.map(e => e.event);
let lastIndex = -1;
const issues = [];
eventNames.forEach(event => {
const index = expectedOrder.indexOf(event);
if (index !== -1) {
if (index < lastIndex) {
issues.push(`${event} fired out of expected order`);
}
lastIndex = index;
}
});
return issues;
}
Duplicate Event Detection
Common Duplicates
- Multiple
page_viewon single page load purchasefiring on page refresh- Click events on bubbling elements
Detection Code
function findDuplicates(events) {
const seen = {};
const duplicates = [];
events.forEach((event, i) => {
if (!event.event) return;
const key = JSON.stringify(event);
if (seen[key]) {
duplicates.push({
event: event.event,
firstIndex: seen[key],
duplicateIndex: i
});
} else {
seen[key] = i;
}
});
return duplicates;
}
Real-time Monitoring Setup
Console Monitoring
// Paste in browser console to monitor pushes
(function() {
const original = dataLayer.push;
dataLayer.push = function() {
console.group('📊 dataLayer.push');
console.log('Data:', arguments[0]);
console.log('Time:', new Date().toISOString());
console.groupEnd();
return original.apply(this, arguments);
};
console.log('✅ DataLayer monitoring active');
})();
Export DataLayer
// Copy full dataLayer to clipboard
copy(JSON.stringify(dataLayer, null, 2));
Validation Checklist
Structure
- dataLayer initialized before GTM
- Using push() not assignment
- Event names follow conventions
- Parameter names follow conventions
Data Types
- value is number
- currency is 3-letter code
- quantity is integer
- items is array
- Required fields present
E-commerce
- ecommerce:null before each push
- items array has item_id and item_name
- transaction_id is unique
- Consistent currency across events
Sequence
- Events fire in logical order
- No duplicate events
- Purchase fires only once
Debug Tools
GTM Preview Mode
- Real-time event inspection
- Variable value checking
- Tag firing verification
GA4 DebugView
- Live event stream
- Parameter validation
- User property tracking
Browser Console
// View current dataLayer
console.table(dataLayer);
// Filter by event
dataLayer.filter(d => d.event === 'purchase');