PageSourceSearch

https://www.datavant.com/wp-content/themes/datavant/zen-blocks/social-media-icons/social-media-icons.js

js datavant.com collected 2026-10-02 05:53:51 UTC 4,770 bytes, 137 lines download raw bytes

1// Social Media Icons - Vanilla JS animations and behavior
2const animateSocialMediaIcons = () => {
3  const socialContainers = document.querySelectorAll('.social-media-icons');
4  if (!socialContainers.length) return;
5
6  // Respect reduced motion
7  const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
8
9  socialContainers.forEach(container => {
10    const socialLinks = container.querySelectorAll('.social-icon-link');
11    
12    if (!prefersReduced && socialLinks.length > 0) {
13      // Set initial states for entrance animation
14      socialLinks.forEach((link, index) => {
15        link.style.opacity = '0';
16        link.style.transform = 'translateY(20px)';
17        link.style.transition = 'opacity 0.6s ease-out, transform 0.6s ease-out';
18      });
19
20      // Create Intersection Observer for scroll-triggered animation
21      const observer = new IntersectionObserver((entries) => {
22        entries.forEach(entry => {
23          if (entry.isIntersecting) {
24            const links = entry.target.querySelectorAll('.social-icon-link');
25            
26            // Animate each link with staggered delay
27            links.forEach((link, index) => {
28              setTimeout(() => {
29                link.style.opacity = '1';
30                link.style.transform = 'translateY(0)';
31              }, index * 100); // 100ms stagger delay
32            });
33            
34            // Unobserve after animation to prevent re-triggering
35            observer.unobserve(entry.target);
36          }
37        });
38      }, {
39        threshold: 0.15, // Trigger when 15% of container is visible
40        rootMargin: '0px 0px -15% 0px' // Equivalent to "top 85%" trigger point
41      });
42
43      // Observe the container
44      observer.observe(container);
45    }
46
47    // Add accessibility enhancements
48    socialLinks.forEach(link => {
49      // Add focus handling for keyboard navigation
50      link.addEventListener('focus', (e) => {
51        if (container.classList.contains('hover-enabled')) {
52          e.target.style.transform = 'translateY(-2px)';
53          e.target.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)';
54        }
55      });
56
57      link.addEventListener('blur', (e) => {
58        if (container.classList.contains('hover-enabled')) {
59          e.target.style.transform = '';
60          e.target.style.boxShadow = '';
61        }
62      });
63
64      // Ensure proper ARIA labels for screen readers
65      const img = link.querySelector('.social-icon');
66      if (img && !link.getAttribute('aria-label')) {
67        const altText = img.getAttribute('alt') || 'Social Media Link';
68        link.setAttribute('aria-label', altText);
69      }
70    });
71
72    // Add ripple effect on click (optional enhancement)
73    if (container.classList.contains('hover-enabled')) {
74      socialLinks.forEach(link => {
75        link.addEventListener('click', (e) => {
76          const wrapper = link.querySelector('.social-icon-wrapper');
77          if (!wrapper) return;
78
79          // Create ripple element
80          const ripple = document.createElement('span');
81          ripple.classList.add('ripple-effect');
82          
83          // Position ripple at click point
84          const rect = wrapper.getBoundingClientRect();
85          const size = Math.max(rect.width, rect.height);
86          const x = e.clientX - rect.left - size / 2;
87          const y = e.clientY - rect.top - size / 2;
88          
89          ripple.style.cssText = `
90            position: absolute;
91            width: ${size}px;
92            height: ${size}px;
93            left: ${x}px;
94            top: ${y}px;
95            background: rgba(255, 255, 255, 0.3);
96            border-radius: 50%;
97            transform: scale(0);
98            animation: ripple-animation 0.6s linear;
99            pointer-events: none;
100            z-index: 1;
101          `;
102
103          // Add CSS animation if not already defined
104          if (!document.querySelector('#ripple-animation-css')) {
105            const style = document.createElement('style');
106            style.id = 'ripple-animation-css';
107            style.textContent = `
108              @keyframes ripple-animation {
109                to {
110                  transform: scale(2);
111                  opacity: 0;
112                }
113              }
114            `;
115            document.head.appendChild(style);
116          }
117
118          wrapper.style.position = 'relative';
119          wrapper.style.overflow = 'hidden';
120          wrapper.appendChild(ripple);
121
122          // Remove ripple after animation
123          setTimeout(() => {
124            if (ripple.parentNode) {
125              ripple.parentNode.removeChild(ripple);
126            }
127          }, 600);
128        });
129      });
130    }
131  });
132};
133
134// Initialize on document ready
135jQuery(document).ready(() => {
136  animateSocialMediaIcons();
137});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.