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