Spaces:
Running
Running
File size: 4,696 Bytes
0a96199 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
// 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;
}
|