Home
Visitor Frequency Personalizer
🔄

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.

PersonalizationIntermediatePersonalization & User Journey

Key Features

Visit frequency tracking
Progressive messaging system
Returning visitor recognition
Custom frequency thresholds
Journey-based personalization
Engagement optimization

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&apos;s something special.',
        offer: '20% off - Frequent visitor bonus'
      },
      
      'loyal': {
        headline: 'You&apos;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.