// Animated Tabs JavaScript - Blinkist Style class AnimatedTabs { constructor(container) { this.container = container; this.tabButtons = container.querySelectorAll('.tab-button'); this.tabPanels = container.querySelectorAll('.tab-panel'); this.tabSlider = container.querySelector('.tab-slider'); this.isAnimating = false; this.init(); } init() { this.setInitialState(); this.bindEvents(); this.addResponsiveBehavior(); } setInitialState() { // Set initial slider position const activeButton = this.container.querySelector('.tab-button.active'); if (activeButton && this.tabSlider) { this.updateSliderPosition(activeButton); } // Ensure first tab is active if none are if (!activeButton && this.tabButtons.length > 0) { this.tabButtons[0].classList.add('active'); this.tabPanels[0].classList.add('active'); this.updateSliderPosition(this.tabButtons[0]); } } bindEvents() { this.tabButtons.forEach(button => { button.addEventListener('click', (e) => { e.preventDefault(); this.switchTab(button); }); }); } switchTab(activeButton) { if (this.isAnimating) return; this.isAnimating = true; const targetTab = activeButton.getAttribute('data-tab'); // Remove active class from all buttons and panels this.tabButtons.forEach(btn => btn.classList.remove('active')); this.tabPanels.forEach(panel => panel.classList.remove('active')); // Add active class to clicked button and target panel activeButton.classList.add('active'); const targetPanel = this.container.querySelector(`#${targetTab}`); if (targetPanel) { targetPanel.classList.add('active'); } // Animate slider this.updateSliderPosition(activeButton); // Trigger custom event this.container.dispatchEvent(new CustomEvent('tabChanged', { detail: { activeTab: targetTab, activeButton: activeButton, activePanel: targetPanel } })); // Reset animation flag after transition setTimeout(() => { this.isAnimating = false; }, 400); } updateSliderPosition(button) { if (!this.tabSlider) return; const buttonRect = button.getBoundingClientRect(); const containerRect = this.container.getBoundingClientRect(); this.tabSlider.style.width = button.offsetWidth + 'px'; this.tabSlider.style.left = (button.offsetLeft - this.container.offsetLeft) + 'px'; } addResponsiveBehavior() { // Handle window resize let resizeTimeout; window.addEventListener('resize', () => { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { const activeButton = this.container.querySelector('.tab-button.active'); if (activeButton) { this.updateSliderPosition(activeButton); } }, 100); }); // Add hover effects for mobile if (window.innerWidth <= 768) { this.tabButtons.forEach(button => { button.addEventListener('mouseenter', () => { if (!button.classList.contains('active')) { button.style.transform = 'scale(1.05)'; } }); button.addEventListener('mouseleave', () => { button.style.transform = 'scale(1)'; }); }); } } // Public methods switchToTab(tabId) { const button = this.container.querySelector(`[data-tab="${tabId}"]`); if (button) { this.switchTab(button); } } getActiveTab() { const activeButton = this.container.querySelector('.tab-button.active'); return activeButton ? activeButton.getAttribute('data-tab') : null; } } // Auto-initialize tabs when DOM is loaded document.addEventListener('DOMContentLoaded', function() { const tabsContainers = document.querySelectorAll('.tabs-wrapper'); tabsContainers.forEach(container => { new AnimatedTabs(container); }); }); // Export for module usage if (typeof module !== 'undefined' && module.exports) { module.exports = AnimatedTabs; }