deepsite / booknap project /animated-tabs.js
aakash1777's picture
Upload 38 files
0a96199 verified
raw
history blame
4.7 kB
// 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;
}