/** * LeafBox Technologies - Main JavaScript * Handles core functionality, theme toggle, navigation, and form interactions */ // DOM Content Loaded document.addEventListener('DOMContentLoaded', function() { initializeApp(); }); /** * Initialize the application */ function initializeApp() { initThemeToggle(); initNavigation(); initScrollEffects(); initFormHandling(); initFestiveCursor(); initSectionReveal(); initHeaderScroll(); } /** * Theme Toggle Functionality */ function initThemeToggle() { const themeToggle = document.getElementById('themeToggle'); const body = document.body; // Get saved theme or default to light const savedTheme = localStorage.getItem('theme') || 'light'; setTheme(savedTheme); themeToggle.addEventListener('click', () => { const currentTheme = body.getAttribute('data-theme') || 'light'; const newTheme = currentTheme === 'light' ? 'dark' : 'light'; setTheme(newTheme); }); function setTheme(theme) { body.setAttribute('data-theme', theme); localStorage.setItem('theme', theme); // Update icons const lightIcon = themeToggle.querySelector('.theme-icon:not(.hidden)'); const darkIcon = themeToggle.querySelector('.theme-icon.hidden'); if (theme === 'dark') { lightIcon?.classList.add('hidden'); darkIcon?.classList.remove('hidden'); } else { lightIcon?.classList.remove('hidden'); darkIcon?.classList.add('hidden'); } } } /** * Navigation Functionality */ function initNavigation() { const navLinks = document.querySelectorAll('.nav-link'); const sections = document.querySelectorAll('section[id]'); // Smooth scrolling for navigation links navLinks.forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const targetId = link.getAttribute('href'); const targetSection = document.querySelector(targetId); if (targetSection) { const headerHeight = document.querySelector('.header').offsetHeight; const targetPosition = targetSection.offsetTop - headerHeight; window.scrollTo({ top: targetPosition, behavior: 'smooth' }); } }); }); // Update active navigation on scroll window.addEventListener('scroll', () => { let current = ''; const scrollPos = window.scrollY + 100; sections.forEach(section => { const sectionTop = section.offsetTop; const sectionHeight = section.offsetHeight; if (scrollPos >= sectionTop && scrollPos < sectionTop + sectionHeight) { current = section.getAttribute('id'); } }); navLinks.forEach(link => { link.classList.remove('active'); if (link.getAttribute('href') === `#${current}`) { link.classList.add('active'); } }); }); } /** * Scroll Effects */ function initScrollEffects() { // Add scroll-based animations const animateOnScroll = () => { const elements = document.querySelectorAll('.service-card, .stat-item'); elements.forEach(element => { const elementTop = element.getBoundingClientRect().top; const elementVisible = 150; if (elementTop < window.innerHeight - elementVisible) { element.style.opacity = '1'; element.style.transform = 'translateY(0)'; } }); }; // Initialize elements with hidden state const elementsToAnimate = document.querySelectorAll('.service-card, .stat-item'); elementsToAnimate.forEach(element => { element.style.opacity = '0'; element.style.transform = 'translateY(30px)'; element.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; }); window.addEventListener('scroll', animateOnScroll); animateOnScroll(); // Run once on load } /** * Form Handling */ function initFormHandling() { const contactForm = document.getElementById('contactForm'); contactForm.addEventListener('submit', handleFormSubmit); function handleFormSubmit(e) { e.preventDefault(); const formData = new FormData(contactForm); const submitBtn = contactForm.querySelector('.submit-btn'); const originalText = submitBtn.innerHTML; // Show loading state submitBtn.innerHTML = '
Sending...'; submitBtn.disabled = true; // Simulate form submission (replace with actual API call) setTimeout(() => { // Success state submitBtn.innerHTML = '✓ Message Sent!'; submitBtn.style.background = '#059669'; // Show success message showSuccessMessage(); // Reset form contactForm.reset(); // Reset button after delay setTimeout(() => { submitBtn.innerHTML = originalText; submitBtn.style.background = ''; submitBtn.disabled = false; }, 3000); }, 2000); } function showSuccessMessage() { const successDiv = document.createElement('div'); successDiv.className = 'success-message'; successDiv.innerHTML = ` Thank you! Your holiday greetings have been sent. We'll be in touch soon! `; contactForm.parentNode.insertBefore(successDiv, contactForm); setTimeout(() => { successDiv.remove(); }, 5000); } // Real-time form validation const inputs = contactForm.querySelectorAll('input, textarea'); inputs.forEach(input => { input.addEventListener('blur', validateField); input.addEventListener('input', clearErrors); }); function validateField(e) { const field = e.target; const value = field.value.trim(); clearFieldError(field); if (field.hasAttribute('required') && !value) { showFieldError(field, 'This field is required'); return false; } if (field.type === 'email' && value) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(value)) { showFieldError(field, 'Please enter a valid email address'); return false; } } return true; } function showFieldError(field, message) { field.style.borderColor = '#C36A6A'; const errorDiv = document.createElement('div'); errorDiv.className = 'field-error'; errorDiv.style.color = '#C36A6A'; errorDiv.style.fontSize = '14px'; errorDiv.style.marginTop = '4px'; errorDiv.textContent = message; field.parentNode.appendChild(errorDiv); } function clearFieldError(field) { field.style.borderColor = ''; const errorDiv = field.parentNode.querySelector('.field-error'); if (errorDiv) { errorDiv.remove(); } } function clearErrors(e) { clearFieldError(e.target); } } /** * Festive Cursor Effect (Desktop Only) */ function initFestiveCursor() { // Only enable on desktop to avoid interfering with touch if (window.innerWidth < 768 || !window.matchMedia('(pointer:fine)').matches) { return; } const cursor = document.createElement('div'); cursor.className = 'festive-cursor'; document.body.appendChild(cursor); let mouseX = 0; let mouseY = 0; let cursorX = 0; let cursorY = 0; document.addEventListener('mousemove', (e) => { mouseX = e.clientX; mouseY = e.clientY; }); function animateCursor() { const deltaX = mouseX - cursorX; const deltaY = mouseY - cursorY; cursorX += deltaX * 0.1; cursorY += deltaY * 0.1; cursor.style.left = cursorX - 10 + 'px'; cursor.style.top = cursorY - 10 + 'px'; requestAnimationFrame(animateCursor); } animateCursor(); } /** * Section Reveal Animation */ function initSectionReveal() { const sections = document.querySelectorAll('.about, .services, .contact'); const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('revealed'); } }); }, observerOptions); sections.forEach(section => { section.classList.add('section-reveal'); observer.observe(section); }); } /** * Header Scroll Effect */ function initHeaderScroll() { const header = document.querySelector('.header'); let lastScrollY = window.scrollY; window.addEventListener('scroll', () => { const currentScrollY = window.scrollY; if (currentScrollY > 100) { header.classList.add('scrolled'); } else { header.classList.remove('scrolled'); } // Hide/show header on scroll if (currentScrollY > lastScrollY && currentScrollY > 200) { header.style.transform = 'translateY(-100%)'; } else { header.style.transform = 'translateY(0)'; } lastScrollY = currentScrollY; }); } /** * Utility Functions */ // Debounce function for performance function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Throttle function for scroll events function throttle(func, limit) { let inThrottle; return function() { const args = arguments; const context = this; if (!inThrottle) { func.apply(context, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } } } // Get element offset function getOffset(element) { const rect = element.getBoundingClientRect(); return { top: rect.top + window.scrollY, left: rect.left + window.scrollX }; } // Check if element is in viewport function isInViewport(element, threshold = 0) { const rect = element.getBoundingClientRect(); const windowHeight = window.innerHeight; const windowWidth = window.innerWidth; return ( rect.top >= -threshold && rect.left >= -threshold && rect.bottom <= windowHeight + threshold && rect.right <= windowWidth + threshold ); } // Performance optimized scroll listener const optimizedScrollHandler = throttle(() => { // Add any scroll-based functionality here }, 16); // ~60fps window.addEventListener('scroll', optimizedScrollHandler); // Handle window resize const optimizedResizeHandler = debounce(() => { // Handle resize logic if (window.innerWidth >= 768) { // Enable desktop features } else { // Enable mobile optimizations } }, 250); window.addEventListener('resize', optimizedResizeHandler); // Page visibility API for performance document.addEventListener('visibilitychange', () => { if (document.hidden) { // Pause animations when page is not visible document.body.style.animationPlayState = 'paused'; } else { // Resume animations when page becomes visible document.body.style.animationPlayState = 'running'; } }); // Error handling window.addEventListener('error', (e) => { console.error('JavaScript Error:', e.error); // You could send error reports to analytics here }); // Handle unhandled promise rejections window.addEventListener('unhandledrejection', (e) => { console.error('Unhandled Promise Rejection:', e.reason); e.preventDefault(); });