Visitor Frequency Personalizer
Journey-Based Content Adaptation
Advanced visitor segmentation system that personalizes content based on visit frequency patterns, creating tailored experiences for first-time, returning, frequent, and loyal visitors to maximize engagement and conversions.
Key Features
Script Overview
What It Does
This script tracks visitor frequency and personalizes content accordingly. Show special offers to returning visitors, different CTAs based on visit count, and progressive messaging that evolves with user engagement.
Use Case
Perfect for showing "Welcome back!" messages, special offers for returning visitors, or different CTAs based on how many times someone has visited.
Key Benefits
- •Improved user experience through familiarity
- •Higher conversion rates for return visitors
- •Progressive messaging strategy
- •Better user journey optimization
- •Enhanced visitor engagement
- •Automated personalization
JavaScript Code
<script>
// Visitor Frequency Personalizer Script
// Version: 1.0
// Last Updated: 2025-09-09
(function() {
'use strict';
var config = {
// Visit frequency tiers
frequencyTiers: {
'first-time': { min: 1, max: 1 },
'returning': { min: 2, max: 5 },
'frequent': { min: 6, max: 15 },
'loyal': { min: 16, max: Infinity }
},
// Content mapping by frequency tier
contentMappings: {
'first-time': {
headline: 'Welcome! Discover What We Can Do For You',
cta: 'Get Started',
message: 'New here? Let us show you around.',
offer: null
},
'returning': {
headline: 'Welcome Back! Ready to Take the Next Step?',
cta: 'Continue Journey',
message: 'Great to see you again. Pick up where you left off.',
offer: '10% off for returning visitors'
},
'frequent': {
headline: 'Thanks for Your Continued Interest!',
cta: 'Claim Special Offer',
message: 'We notice you keep coming back. Here's something special.',
offer: '20% off - Frequent visitor bonus'
},
'loyal': {
headline: 'You're One of Our VIPs!',
cta: 'Access VIP Benefits',
message: 'Your loyalty deserves recognition. Enjoy exclusive access.',
offer: 'VIP pricing - 30% off everything'
}
},
// Element selectors for personalization
selectors: {
headline: '[data-visitor-headline], .visitor-headline, h1.hero-title',
cta: '[data-visitor-cta], .visitor-cta, .cta-button',
message: '[data-visitor-message], .visitor-message',
offer: '[data-visitor-offer], .visitor-offer'
},
// Session gap before counting as new visit (minutes)
sessionGap: 30,
// Storage keys
storageKeys: {
visitData: 'visitor_frequency_data',
lastVisit: 'visitor_last_visit'
},
// Animation settings
animation: {
enabled: true,
duration: 400,
easing: 'ease-out'
},
debug: false
};
var visitorData = null;
function debugLog(message, data) {
if (config.debug) {
console.log('[Visitor Personalizer] ' + message, data || '');
}
}
function getVisitorData() {
try {
var stored = localStorage.getItem(config.storageKeys.visitData);
if (stored) {
return JSON.parse(stored);
}
} catch (e) {
debugLog('Error reading visitor data:', e);
}
// Return default visitor data
return {
totalVisits: 0,
firstVisit: Date.now(),
lastVisit: null,
visitDates: [],
currentSession: null
};
}
function updateVisitorData() {
var now = Date.now();
var data = getVisitorData();
var isNewVisit = false;
// Check if this is a new visit (based on session gap)
if (!data.lastVisit || (now - data.lastVisit) > (config.sessionGap * 60 * 1000)) {
isNewVisit = true;
data.totalVisits += 1;
data.visitDates.push(now);
// Keep only last 50 visits to avoid storage bloat
if (data.visitDates.length > 50) {
data.visitDates = data.visitDates.slice(-50);
}
}
data.lastVisit = now;
data.currentSession = now;
// Save updated data
try {
localStorage.setItem(config.storageKeys.visitData, JSON.stringify(data));
localStorage.setItem(config.storageKeys.lastVisit, now.toString());
} catch (e) {
debugLog('Error saving visitor data:', e);
}
debugLog('Visitor data updated:', { data: data, isNewVisit: isNewVisit });
return { data: data, isNewVisit: isNewVisit };
}
function getFrequencyTier(visitCount) {
for (var tier in config.frequencyTiers) {
var range = config.frequencyTiers[tier];
if (visitCount >= range.min && visitCount <= range.max) {
return tier;
}
}
return 'first-time'; // Default fallback
}
function getContentForTier(tier) {
return config.contentMappings[tier] || config.contentMappings['first-time'];
}
function animateContentChange(element, newContent, contentType) {
if (!config.animation.enabled || !element) {
updateElementContent(element, newContent, contentType);
return;
}
// Slide and fade effect
element.style.transition = 'all ' + config.animation.duration + 'ms ' + config.animation.easing;
element.style.opacity = '0';
element.style.transform = 'translateY(-10px)';
setTimeout(function() {
updateElementContent(element, newContent, contentType);
// Animate in
element.style.opacity = '1';
element.style.transform = 'translateY(0)';
// Clean up
setTimeout(function() {
element.style.transition = '';
element.style.transform = '';
}, config.animation.duration);
}, config.animation.duration / 2);
}
function updateElementContent(element, newContent, contentType) {
if (!element || !newContent) return;
// Store original content if not already stored
if (!element.getAttribute('data-original-' + contentType)) {
element.setAttribute('data-original-' + contentType, element.textContent || '');
}
// Update content
element.textContent = newContent;
element.setAttribute('data-visitor-tier', visitorData.tier);
element.setAttribute('data-visitor-visits', visitorData.visitCount);
debugLog('Content updated:', {
type: contentType,
content: newContent,
tier: visitorData.tier
});
}
function personalizeContent(tier, content) {
Object.keys(config.selectors).forEach(function(contentType) {
var selector = config.selectors[contentType];
var newContent = content[contentType];
if (!selector || !newContent) return;
var elements = document.querySelectorAll(selector);
Array.prototype.forEach.call(elements, function(element) {
// Skip if already personalized in this session
if (element.getAttribute('data-visitor-personalized') === 'true') return;
if (config.animation.enabled) {
animateContentChange(element, newContent, contentType);
} else {
updateElementContent(element, newContent, contentType);
}
element.setAttribute('data-visitor-personalized', 'true');
});
});
}
function showSpecialOffer(offer) {
if (!offer) return;
var offerElements = document.querySelectorAll(config.selectors.offer);
Array.prototype.forEach.call(offerElements, function(element) {
element.textContent = offer;
element.style.display = 'block';
element.setAttribute('data-visitor-offer-active', 'true');
// Add some styling for visibility
element.style.backgroundColor = '#f0f8ff';
element.style.padding = '10px';
element.style.border = '2px solid #007cba';
element.style.borderRadius = '5px';
element.style.marginTop = '15px';
});
debugLog('Special offer displayed:', offer);
}
function trackVisitorPersonalization(tier, visitCount, isNewVisit) {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'visitor_personalized',
visitor_tier: tier,
visitor_visit_count: visitCount,
visitor_is_new_visit: isNewVisit,
visitor_first_visit: visitorData.firstVisit,
visitor_days_since_first: Math.round((Date.now() - visitorData.firstVisit) / (1000 * 60 * 60 * 24)),
page_url: window.location.href
});
debugLog('Visitor personalization tracked:', {
tier: tier,
visitCount: visitCount,
isNewVisit: isNewVisit
});
}
function getVisitorInsights() {
var data = visitorData;
var daysSinceFirst = Math.round((Date.now() - data.firstVisit) / (1000 * 60 * 60 * 24));
var avgDaysBetweenVisits = data.visitCount > 1 ? daysSinceFirst / (data.visitCount - 1) : 0;
return {
tier: data.tier,
visitCount: data.visitCount,
daysSinceFirst: daysSinceFirst,
avgDaysBetweenVisits: Math.round(avgDaysBetweenVisits),
isFrequentVisitor: data.visitCount >= 6,
isLoyalVisitor: data.visitCount >= 16
};
}
function initVisitorPersonalization() {
debugLog('Initializing Visitor Frequency Personalizer');
var result = updateVisitorData();
visitorData = result.data;
var isNewVisit = result.isNewVisit;
var tier = getFrequencyTier(visitorData.totalVisits);
var content = getContentForTier(tier);
// Add tier and visit count to visitor data
visitorData.tier = tier;
visitorData.visitCount = visitorData.totalVisits;
debugLog('Visitor classified:', {
tier: tier,
visitCount: visitorData.totalVisits,
isNewVisit: isNewVisit
});
// Wait a moment for DOM to be ready
setTimeout(function() {
personalizeContent(tier, content);
// Show special offer if available
if (content.offer) {
showSpecialOffer(content.offer);
}
// Track personalization
trackVisitorPersonalization(tier, visitorData.totalVisits, isNewVisit);
}, 100);
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initVisitorPersonalization);
} else {
initVisitorPersonalization();
}
// Expose utility functions
window.visitorFrequencyPersonalizer = {
getVisitorData: function() {
return visitorData;
},
getVisitorInsights: function() {
return getVisitorInsights();
},
getCurrentTier: function() {
return visitorData ? visitorData.tier : null;
},
resetVisitorData: function() {
localStorage.removeItem(config.storageKeys.visitData);
localStorage.removeItem(config.storageKeys.lastVisit);
debugLog('Visitor data reset');
},
forceRefresh: function() {
// Remove personalization flags and re-run
var personalizedElements = document.querySelectorAll('[data-visitor-personalized]');
Array.prototype.forEach.call(personalizedElements, function(element) {
element.removeAttribute('data-visitor-personalized');
});
initVisitorPersonalization();
}
};
})();
</script>
💡 Pro Tip: Copy the entire code block above and paste it directly into a GTM Custom HTML tag. The script is self-contained and ready to use immediately.