Spaces:
Running
Running
| // Counter animation for statistics | |
| function animateCounters() { | |
| const counters = document.querySelectorAll('.counter'); | |
| counters.forEach(counter => { | |
| const target = parseInt(counter.getAttribute('data-target')); | |
| const increment = target / 100; | |
| let current = 0; | |
| const updateCounter = () => { | |
| current += increment; | |
| if (current < target) { | |
| counter.textContent = Math.ceil(current); | |
| requestAnimationFrame(updateCounter); | |
| } else { | |
| counter.textContent = target; | |
| } | |
| }; | |
| updateCounter(); | |
| }); | |
| } | |
| // Intersection Observer for animations | |
| const observerOptions = { | |
| threshold: 0.1, | |
| rootMargin: '0px 0px -100px 0px' | |
| }; | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.classList.add('animate-fade-in'); | |
| // Trigger counter animation when stats section is visible | |
| if (entry.target.querySelector('.counter')) { | |
| animateCounters(); | |
| } | |
| } | |
| }); | |
| }, observerOptions); | |
| // Observe all sections | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const sections = document.querySelectorAll('section'); | |
| sections.forEach(section => observer.observe(section)); | |
| }); | |
| // Product filtering | |
| function filterProducts(category) { | |
| const products = document.querySelectorAll('#productsGrid .product-card'); | |
| const buttons = document.querySelectorAll('button[onclick^="filterProducts"]'); | |
| // Update active button | |
| buttons.forEach(btn => { | |
| btn.classList.remove('tab-active'); | |
| if (btn.getAttribute('onclick').includes(category)) { | |
| btn.classList.add('tab-active'); | |
| } | |
| }); | |
| // Filter products | |
| products.forEach(product => { | |
| if (category === 'all' || product.getAttribute('data-category') === category) { | |
| product.style.display = 'block'; | |
| setTimeout(() => { | |
| product.style.opacity = '1'; | |
| product.style.transform = 'scale(1)'; | |
| }, 10); | |
| } else { | |
| product.style.opacity = '0'; | |
| product.style.transform = 'scale(0.9)'; | |
| setTimeout(() => { | |
| product.style.display = 'none'; | |
| }, 300); | |
| } | |
| }); | |
| } | |
| // Smooth scroll for anchor links | |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { | |
| anchor.addEventListener('click', function (e) { | |
| e.preventDefault(); | |
| const target = document.querySelector(this.getAttribute('href')); | |
| if (target) { | |
| target.scrollIntoView({ | |
| behavior: 'smooth', | |
| block: 'start' | |
| }); | |
| } | |
| }); | |
| }); | |
| // Parallax effect for hero section | |
| window.addEventListener('scroll', () => { | |
| const scrolled = window.pageYOffset; | |
| const parallax = document.querySelector('.hero-section'); | |
| if (parallax) { | |
| parallax.style.transform = `translateY(${scrolled * 0.5}px)`; | |
| } | |
| }); | |
| // Mobile menu toggle (if needed) | |
| function toggleMobileMenu() { | |
| const menu = document.querySelector('.mobile-menu'); | |
| if (menu) { | |
| menu.classList.toggle('active'); | |
| } | |
| } | |
| // Form validation | |
| function validateForm(form) { | |
| const inputs = form.querySelectorAll('input[required], textarea[required], select[required]'); | |
| let isValid = true; | |
| inputs.forEach(input => { | |
| if (!input.value.trim()) { | |
| input.classList.add('border-red-500'); | |
| isValid = false; | |
| } else { | |
| input.classList.remove('border-red-500'); | |
| } | |
| }); | |
| return isValid; | |
| } | |
| // Email validation | |
| function validateEmail(email) { | |
| const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
| return re.test(email); | |
| } | |
| // Phone number formatting | |
| function formatPhoneNumber(input) { | |
| const phoneNumber = input.value.replace(/\D/g, ''); | |
| const formattedNumber = phoneNumber.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3'); | |
| input.value = formattedNumber; | |
| } | |
| // Lazy loading for images | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const images = document.querySelectorAll('img[data-src]'); | |
| const imageObserver = new IntersectionObserver((entries, observer) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| const img = entry.target; | |
| img.src = img.dataset.src; | |
| img.classList.remove('lazy'); | |
| imageObserver.unobserve(img); | |
| } | |
| }); | |
| }); | |
| images.forEach(img => imageObserver.observe(img)); | |
| }); | |
| // Sticky header | |
| window.addEventListener('scroll', () => { | |
| const header = document.querySelector('header'); | |
| if (header) { | |
| if (window.scrollY > 100) { | |
| header.classList.add('shadow-lg'); | |
| } else { | |
| header.classList.remove('shadow-lg'); | |
| } | |
| } | |
| }); | |
| // Copy to clipboard functionality | |
| function copyToClipboard(text) { | |
| navigator.clipboard.writeText(text).then(() => { | |
| showNotification('Copied to clipboard!'); | |
| }); | |
| } | |
| // Show notification | |
| function showNotification(message, type = 'success') { | |
| const notification = document.createElement('div'); | |
| notification.className = `fixed top-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 animate-fade-in ${ | |
| type === 'success' ? 'bg-green-500 text-white' : 'bg-red-500 text-white' | |
| }`; | |
| notification.textContent = message; | |
| document.body.appendChild(notification); | |
| setTimeout(() => { | |
| notification.style.opacity = '0'; | |
| setTimeout(() => { | |
| document.body.removeChild(notification); | |
| }, 300); | |
| }, 3000); | |
| } | |
| // Dark mode toggle | |
| function toggleDarkMode() { | |
| document.body.classList.toggle('dark-mode'); | |
| localStorage.setItem('darkMode', document.body.classList.contains('dark-mode')); | |
| } | |
| // Load dark mode preference | |
| document.addEventListener('DOMContentLoaded', () => { | |
| if (localStorage.getItem('darkMode') === 'true') { | |
| document.body.classList.add('dark-mode'); | |
| } | |
| }); | |
| // Initialize tooltips | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const tooltips = document.querySelectorAll('[data-tooltip]'); | |
| tooltips.forEach(element => { | |
| element.addEventListener('mouseenter', (e) => { | |
| const tooltip = document.createElement('div'); | |
| tooltip.className = 'absolute bg-gray-800 text-white text-sm px-2 py-1 rounded z-50'; | |
| tooltip.textContent = e.target.getAttribute('data-tooltip'); | |
| tooltip.style.top = `${e.target.offsetTop - 30}px`; | |
| tooltip.style.left = `${e.target.offsetLeft}px`; | |
| tooltip.id = 'tooltip'; | |
| document.body.appendChild(tooltip); | |
| }); | |
| element.addEventListener('mouseleave', () => { | |
| const tooltip = document.getElementById('tooltip'); | |
| if (tooltip) { | |
| document.body.removeChild(tooltip); | |
| } | |
| }); | |
| }); | |
| }); | |
| // Accordion functionality | |
| function toggleAccordion(element) { | |
| const content = element.nextElementSibling; | |
| const icon = element.querySelector('.accordion-icon'); | |
| content.classList.toggle('hidden'); | |
| icon.classList.toggle('rotate-180'); | |
| } | |
| // Tab functionality | |
| function switchTab(tabId, contentId) { | |
| // Hide all tab contents | |
| const contents = document.querySelectorAll('[id^="tab-content-"]'); | |
| contents.forEach(content => content.classList.add('hidden')); | |
| // Remove active class from all tabs | |
| const tabs = document.querySelectorAll('[id^="tab-"]'); | |
| tabs.forEach(tab => tab.classList.remove('active')); | |
| // Show selected content | |
| document.getElementById(contentId).classList.remove('hidden'); | |
| // Add active class to selected tab | |
| document.getElementById(tabId).classList.add('active'); | |
| } | |
| // Search functionality | |
| function searchItems(searchTerm, items) { | |
| const filtered = items.filter(item => | |
| item.textContent.toLowerCase().includes(searchTerm.toLowerCase()) | |
| ); | |
| return filtered; | |
| } | |
| // Debounce function for search | |
| function debounce(func, wait) { | |
| let timeout; | |
| return function executedFunction(...args) { | |
| const later = () => { | |
| clearTimeout(timeout); | |
| func(...args); | |
| }; | |
| clearTimeout(timeout); | |
| timeout = setTimeout(later, wait); | |
| }; | |
| } | |
| // Performance monitoring | |
| window.addEventListener('load', () => { | |
| if ('performance' in window) { | |
| const perfData = window.performance.timing; | |
| const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart; | |
| console.log(`Page load time: ${pageLoadTime}ms`); | |
| } | |
| }); | |
| // Error handling | |
| window.addEventListener('error', (e) => { | |
| console.error('JavaScript error:', e.error); | |
| // You can send errors to a monitoring service here | |
| }); | |
| // Service worker registration for PWA | |
| if ('serviceWorker' in navigator) { | |
| window.addEventListener('load', () => { | |
| navigator.serviceWorker.register('/sw.js') | |
| .then(registration => { | |
| console.log('SW registered: ', registration); | |
| }) | |
| .catch(registrationError => { | |
| console.log('SW registration failed: ', registrationError); | |
| }); | |
| }); | |
| } |