@zaplier/sdk 1.7.4 โ†’ 1.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -19566,58 +19566,19 @@ class AutoTracker {
19566
19566
  console.log("[๐Ÿ‘† AutoTracker] Click delegation handler added");
19567
19567
  }
19568
19568
  }
19569
- // CRITICAL FIX: Scroll delegation with enhanced detection
19569
+ // MODERN APPROACH: IntersectionObserver + Scroll Fallback (2025 Best Practice)
19570
19570
  if (this.config.trackScrolls) {
19571
- const scrollHandler = this.createEnhancedScrollHandler();
19572
- this.delegationHandlers.set('scroll', scrollHandler);
19573
- // Always use document for scroll events (window doesn't work with removeEventListener)
19574
- document.addEventListener('scroll', scrollHandler, this.getEventOptions('scroll'));
19575
19571
  if (this.config.debug) {
19576
- console.log("[๐Ÿ“œ AutoTracker] โœ… Scroll delegation handler added to document");
19577
- console.log("[๐Ÿ“œ AutoTracker] ๐Ÿ“‹ Event listener details:", {
19578
- target: 'document',
19579
- eventType: 'scroll',
19580
- handlerType: typeof scrollHandler,
19581
- options: this.getEventOptions('scroll'),
19582
- handlerStored: this.delegationHandlers.has('scroll')
19583
- });
19584
- // Test if handler is actually working
19585
- const testHandler = () => {
19586
- console.log("[๐Ÿ“œ AutoTracker] ๐Ÿงช MANUAL SCROLL TEST - Handler should trigger");
19587
- scrollHandler(new Event('scroll'));
19588
- };
19589
- // Expose test function globally for manual testing
19590
- if (typeof window !== 'undefined') {
19591
- window.testScrollHandler = testHandler;
19592
- console.log("[๐Ÿ“œ AutoTracker] ๐Ÿงช Manual test available: window.testScrollHandler()");
19593
- }
19594
- // Immediate test of element detection
19595
- setTimeout(() => {
19596
- const scrollElements = this.getCachedScrollElements();
19597
- console.log(`[๐Ÿ“œ AutoTracker] ๐Ÿ“ Initial scroll elements check: ${scrollElements.length} found`);
19598
- if (scrollElements.length > 0) {
19599
- Array.from(scrollElements).forEach((element, index) => {
19600
- console.log(`[๐Ÿ“œ AutoTracker] Element ${index + 1}:`, {
19601
- id: element.id || 'no-id',
19602
- trackEvent: element.getAttribute('data-track-scroll'),
19603
- threshold: element.getAttribute('data-scroll-threshold') || '0.5',
19604
- tagName: element.tagName,
19605
- rect: element.getBoundingClientRect(),
19606
- isVisible: element.getBoundingClientRect().height > 0
19607
- });
19608
- });
19609
- }
19610
- // Also test manually trigger one element
19611
- if (scrollElements.length > 0) {
19612
- const firstElement = scrollElements[0];
19613
- if (firstElement) {
19614
- console.log(`[๐Ÿ“œ AutoTracker] ๐Ÿงช Testing manual trigger on first element:`, firstElement.getAttribute('data-track-scroll'));
19615
- setTimeout(() => {
19616
- console.log("[๐Ÿ“œ AutoTracker] ๐Ÿงช Manual trigger test executed");
19617
- }, 500);
19618
- }
19619
- }
19620
- }, 100);
19572
+ console.log("[๐Ÿ”„ AutoTracker] Setting up modern scroll tracking with IntersectionObserver");
19573
+ }
19574
+ // PRIMARY: Modern IntersectionObserver approach (recommended 2025)
19575
+ this.setupIntersectionObserverScrollTracking();
19576
+ // FALLBACK: Traditional scroll listener for edge cases
19577
+ this.setupFallbackScrollListener();
19578
+ if (this.config.debug) {
19579
+ console.log("[๐Ÿ“œ AutoTracker] โœ… Modern scroll tracking setup complete:");
19580
+ console.log("[๐Ÿ“œ AutoTracker] ๐Ÿ“‹ Primary: IntersectionObserver (modern, performant)");
19581
+ console.log("[๐Ÿ“œ AutoTracker] ๐Ÿ“‹ Fallback: Traditional scroll listener (compatibility)");
19621
19582
  }
19622
19583
  }
19623
19584
  // Setup debounced mutation observer for dynamic content
@@ -19751,7 +19712,209 @@ class AutoTracker {
19751
19712
  return null;
19752
19713
  }
19753
19714
  /**
19754
- * Create enhanced scroll handler with better performance
19715
+ * Modern IntersectionObserver-based scroll tracking (2025 Best Practice)
19716
+ * More performant and reliable than traditional scroll listeners
19717
+ */
19718
+ setupIntersectionObserverScrollTracking() {
19719
+ if (typeof window === 'undefined' || !('IntersectionObserver' in window)) {
19720
+ if (this.config.debug) {
19721
+ console.log("[โš ๏ธ AutoTracker] IntersectionObserver not available, using fallback only");
19722
+ }
19723
+ return;
19724
+ }
19725
+ // Get all scroll elements
19726
+ const scrollElements = document.querySelectorAll('[data-track-scroll]');
19727
+ if (scrollElements.length === 0) {
19728
+ if (this.config.debug) {
19729
+ console.log("[๐Ÿ“ AutoTracker] No scroll elements found for IntersectionObserver");
19730
+ }
19731
+ return;
19732
+ }
19733
+ // Create observer with multiple thresholds for precise tracking
19734
+ const observerOptions = {
19735
+ root: null, // Use viewport as root
19736
+ rootMargin: '0px',
19737
+ threshold: [0, 0.1, 0.25, 0.5, 0.75, 0.9, 1.0] // Multiple thresholds for accuracy
19738
+ };
19739
+ const observer = new IntersectionObserver((entries) => {
19740
+ entries.forEach((entry) => {
19741
+ const element = entry.target;
19742
+ const eventName = element.getAttribute('data-track-scroll');
19743
+ const customThreshold = parseFloat(element.getAttribute('data-scroll-threshold') || '0.5');
19744
+ const hasTriggered = element.getAttribute('data-scroll-triggered') === 'true';
19745
+ if (!eventName || hasTriggered)
19746
+ return;
19747
+ const visibilityRatio = entry.intersectionRatio;
19748
+ if (this.config.debug) {
19749
+ console.log(`[๐Ÿ” IntersectionObserver] Element "${eventName}":`, {
19750
+ elementId: element.id || 'no-id',
19751
+ visibilityRatio: Math.round(visibilityRatio * 1000) / 1000,
19752
+ threshold: customThreshold,
19753
+ isIntersecting: entry.isIntersecting,
19754
+ shouldTrigger: visibilityRatio >= customThreshold
19755
+ });
19756
+ }
19757
+ // Trigger event when visibility threshold is met
19758
+ if (visibilityRatio >= customThreshold) {
19759
+ element.setAttribute('data-scroll-triggered', 'true');
19760
+ const metadata = this.extractMetadata(element);
19761
+ this.trackEvent(eventName, {
19762
+ type: 'scroll',
19763
+ method: 'IntersectionObserver',
19764
+ element: element.tagName.toLowerCase(),
19765
+ threshold: customThreshold,
19766
+ visibilityRatio: Math.round(visibilityRatio * 1000) / 1000,
19767
+ enhanced: true,
19768
+ modern: true,
19769
+ ...metadata,
19770
+ });
19771
+ if (this.config.debug) {
19772
+ console.log(`[๐ŸŽฏ IntersectionObserver] Event "${eventName}" triggered! (${Math.round(visibilityRatio * 100)}% visible)`);
19773
+ }
19774
+ // Unobserve after triggering (one-time trigger)
19775
+ observer.unobserve(element);
19776
+ }
19777
+ });
19778
+ }, observerOptions);
19779
+ // Observe all scroll elements
19780
+ scrollElements.forEach((element) => {
19781
+ observer.observe(element);
19782
+ if (this.config.debug) {
19783
+ console.log(`[๐Ÿ‘๏ธ IntersectionObserver] Now observing: ${element.getAttribute('data-track-scroll')} (${element.id || 'no-id'})`);
19784
+ }
19785
+ });
19786
+ // Store observer for cleanup
19787
+ this.intersectionObserver = observer;
19788
+ if (this.config.debug) {
19789
+ console.log(`[โœ… IntersectionObserver] Setup complete - observing ${scrollElements.length} elements`);
19790
+ }
19791
+ }
19792
+ /**
19793
+ * Fallback scroll listener for compatibility (React/Next.js issues)
19794
+ */
19795
+ setupFallbackScrollListener() {
19796
+ if (typeof window === 'undefined')
19797
+ return;
19798
+ const fallbackHandler = this.createThrottledScrollHandler();
19799
+ // Try multiple attachment strategies for React/Next.js compatibility
19800
+ const attachStrategies = [
19801
+ () => window.addEventListener('scroll', fallbackHandler, { passive: true }),
19802
+ () => document.addEventListener('scroll', fallbackHandler, { passive: true }),
19803
+ () => window.addEventListener('scroll', fallbackHandler, { passive: true, capture: true }),
19804
+ () => {
19805
+ // Delayed attachment for React hydration
19806
+ setTimeout(() => {
19807
+ window.addEventListener('scroll', fallbackHandler, { passive: true });
19808
+ }, 100);
19809
+ },
19810
+ () => {
19811
+ // Document ready state check
19812
+ if (document.readyState === 'complete') {
19813
+ window.addEventListener('scroll', fallbackHandler, { passive: true });
19814
+ }
19815
+ else {
19816
+ window.addEventListener('load', () => {
19817
+ window.addEventListener('scroll', fallbackHandler, { passive: true });
19818
+ });
19819
+ }
19820
+ }
19821
+ ];
19822
+ // Try each strategy
19823
+ attachStrategies.forEach((strategy, index) => {
19824
+ try {
19825
+ strategy();
19826
+ if (this.config.debug) {
19827
+ console.log(`[๐Ÿ“œ AutoTracker] Fallback scroll strategy ${index + 1} attached`);
19828
+ }
19829
+ }
19830
+ catch (error) {
19831
+ if (this.config.debug) {
19832
+ console.log(`[โš ๏ธ AutoTracker] Fallback strategy ${index + 1} failed:`, error);
19833
+ }
19834
+ }
19835
+ });
19836
+ this.delegationHandlers.set('scroll', fallbackHandler);
19837
+ // Test scroll detection after setup
19838
+ if (this.config.debug) {
19839
+ setTimeout(() => {
19840
+ console.log("[๐Ÿงช AutoTracker] Testing fallback scroll in 2 seconds...");
19841
+ // Add one-time debug listener
19842
+ const testListener = () => {
19843
+ console.log("[โœ… AutoTracker] Fallback scroll listener working!");
19844
+ };
19845
+ window.addEventListener('scroll', testListener, { once: true, passive: true });
19846
+ // Trigger test scroll
19847
+ const originalScrollY = window.scrollY;
19848
+ window.scrollBy(0, 1);
19849
+ setTimeout(() => window.scrollTo(0, originalScrollY), 50);
19850
+ }, 2000);
19851
+ }
19852
+ }
19853
+ /**
19854
+ * Create throttled scroll handler (optimized for performance)
19855
+ */
19856
+ createThrottledScrollHandler() {
19857
+ let lastKnownScrollPosition = 0;
19858
+ let ticking = false;
19859
+ return () => {
19860
+ lastKnownScrollPosition = window.scrollY;
19861
+ if (!ticking) {
19862
+ // Use setTimeout throttling (MDN recommended 2025)
19863
+ setTimeout(() => {
19864
+ if (this.config.debug) {
19865
+ console.log(`[๐Ÿ“œ AutoTracker] Fallback scroll handler triggered (scrollY: ${lastKnownScrollPosition})`);
19866
+ }
19867
+ this.processFallbackScroll(lastKnownScrollPosition);
19868
+ ticking = false;
19869
+ }, 20); // 20ms throttle (MDN recommendation)
19870
+ ticking = true;
19871
+ }
19872
+ };
19873
+ }
19874
+ /**
19875
+ * Process scroll for fallback handler (when IntersectionObserver fails)
19876
+ */
19877
+ processFallbackScroll(scrollPosition) {
19878
+ // Only process if IntersectionObserver didn't handle it
19879
+ const scrollElements = document.querySelectorAll('[data-track-scroll]:not([data-scroll-triggered="true"])');
19880
+ if (scrollElements.length === 0)
19881
+ return;
19882
+ scrollElements.forEach((element) => {
19883
+ const eventName = element.getAttribute('data-track-scroll');
19884
+ const threshold = parseFloat(element.getAttribute('data-scroll-threshold') || '0.5');
19885
+ if (!eventName)
19886
+ return;
19887
+ const rect = element.getBoundingClientRect();
19888
+ const windowHeight = window.innerHeight;
19889
+ const elementHeight = rect.height;
19890
+ if (elementHeight <= 0)
19891
+ return;
19892
+ const visibleTop = Math.max(rect.top, 0);
19893
+ const visibleBottom = Math.min(rect.bottom, windowHeight);
19894
+ const visibleHeight = Math.max(0, visibleBottom - visibleTop);
19895
+ const visibilityRatio = visibleHeight / elementHeight;
19896
+ if (visibilityRatio >= threshold) {
19897
+ element.setAttribute('data-scroll-triggered', 'true');
19898
+ const metadata = this.extractMetadata(element);
19899
+ this.trackEvent(eventName, {
19900
+ type: 'scroll',
19901
+ method: 'fallback_scroll',
19902
+ element: element.tagName.toLowerCase(),
19903
+ threshold,
19904
+ visibilityRatio: Math.round(visibilityRatio * 1000) / 1000,
19905
+ scrollPosition,
19906
+ fallback: true,
19907
+ ...metadata,
19908
+ });
19909
+ if (this.config.debug) {
19910
+ console.log(`[๐ŸŽฏ Fallback] Event "${eventName}" triggered! (${Math.round(visibilityRatio * 100)}% visible)`);
19911
+ }
19912
+ }
19913
+ });
19914
+ }
19915
+ /**
19916
+ * LEGACY: Create enhanced scroll handler with better performance
19917
+ * @deprecated Use IntersectionObserver approach instead
19755
19918
  */
19756
19919
  createEnhancedScrollHandler() {
19757
19920
  return () => {
@@ -20030,11 +20193,25 @@ class AutoTracker {
20030
20193
  */
20031
20194
  stop() {
20032
20195
  if (this.config.debug) {
20033
- console.log('[AutoTracker] Stopping auto tracking and cleaning up...');
20196
+ console.log('[๐Ÿ“œ AutoTracker] Stopping enhanced auto tracking and cleaning up...');
20034
20197
  }
20035
- // Remove delegation handlers
20198
+ // Remove delegation handlers from correct targets
20036
20199
  this.delegationHandlers.forEach((handler, eventType) => {
20037
- document.removeEventListener(eventType, handler);
20200
+ if (eventType === 'click') {
20201
+ const delegationTarget = this.getOptimalDelegationTarget();
20202
+ delegationTarget.removeEventListener('click', handler);
20203
+ }
20204
+ else if (eventType === 'scroll') {
20205
+ // Remove from both window and document (as we added to both)
20206
+ window.removeEventListener('scroll', handler);
20207
+ document.removeEventListener('scroll', handler);
20208
+ if (this.config.debug) {
20209
+ console.log('[๐Ÿ“œ AutoTracker] Removed scroll listeners from window and document');
20210
+ }
20211
+ }
20212
+ else {
20213
+ document.removeEventListener(eventType, handler);
20214
+ }
20038
20215
  });
20039
20216
  this.delegationHandlers.clear();
20040
20217
  // Disconnect observers
@@ -20049,6 +20226,9 @@ class AutoTracker {
20049
20226
  // Clear caches
20050
20227
  this.observedElements.clear();
20051
20228
  this.isInitialized = false;
20229
+ if (this.config.debug) {
20230
+ console.log('[๐Ÿ“œ AutoTracker] Enhanced cleanup completed');
20231
+ }
20052
20232
  }
20053
20233
  /**
20054
20234
  * Legacy method - now handled by intelligent mutation observer
@@ -20419,6 +20599,33 @@ class AutoTracker {
20419
20599
  source: 'manual_diagnostic',
20420
20600
  timestamp: Date.now()
20421
20601
  });
20602
+ },
20603
+ testIntersectionObserver: () => {
20604
+ console.log(`[๐Ÿงช AutoTracker] Testing IntersectionObserver setup`);
20605
+ const scrollElements = document.querySelectorAll('[data-track-scroll]');
20606
+ console.log(`Found ${scrollElements.length} scroll elements for IntersectionObserver`);
20607
+ if (this.intersectionObserver && scrollElements.length > 0) {
20608
+ const firstElement = scrollElements[0];
20609
+ if (firstElement) {
20610
+ console.log('Testing first element:', firstElement.getAttribute('data-track-scroll'));
20611
+ // Temporarily remove triggered state for testing
20612
+ firstElement.removeAttribute('data-scroll-triggered');
20613
+ // Re-observe for testing
20614
+ this.intersectionObserver.observe(firstElement);
20615
+ console.log('Re-observing element for test...');
20616
+ }
20617
+ }
20618
+ },
20619
+ testScrollFallback: () => {
20620
+ console.log(`[๐Ÿงช AutoTracker] Testing scroll fallback mechanism`);
20621
+ const handler = this.delegationHandlers.get('scroll');
20622
+ if (handler) {
20623
+ console.log('Triggering fallback scroll handler...');
20624
+ handler(new Event('scroll'));
20625
+ }
20626
+ else {
20627
+ console.log('No scroll handler found in delegation handlers');
20628
+ }
20422
20629
  }
20423
20630
  };
20424
20631
  // Expose tests globally for easy access
@@ -20473,7 +20680,9 @@ class AutoTracker {
20473
20680
  usage: {
20474
20681
  triggerScrollHandler: 'window.autoTrackerTests.triggerScrollHandler()',
20475
20682
  forceScrollEvent: 'window.autoTrackerTests.forceScrollEvent("[data-track-scroll]")',
20476
- testTrackEvent: 'window.autoTrackerTests.testTrackEvent("my_event")'
20683
+ testTrackEvent: 'window.autoTrackerTests.testTrackEvent("my_event")',
20684
+ testIntersectionObserver: 'window.autoTrackerTests.testIntersectionObserver()',
20685
+ testScrollFallback: 'window.autoTrackerTests.testScrollFallback()'
20477
20686
  },
20478
20687
  description: 'Use these functions in the browser console to manually test the AutoTracker'
20479
20688
  }