Skip to content

Instantly share code, notes, and snippets.

@DevShahidul
Last active January 10, 2026 11:33
Show Gist options
  • Select an option

  • Save DevShahidul/76a3b3ae9d8b81aa18d52bc5bbc7c666 to your computer and use it in GitHub Desktop.

Select an option

Save DevShahidul/76a3b3ae9d8b81aa18d52bc5bbc7c666 to your computer and use it in GitHub Desktop.
Dynamic Tab function to use on multiple sections
(function($) {
'use strict';
const DEFAULTS = {
container: '.e-parent',
trigger: '.tabs-nav-item',
contentWrapper: '.tab-content-container',
content: '.tab-content',
eventType: 'click',
speed: 300,
hoverDelay: 150,
isActiveFirstItem: true,
activeClass: 'is-active',
shownClass: 'shown',
videoWrapper: '.video-wrapper',
videoSelector: 'video'
};
/**
* Tab Manager Class
*/
class TabManager {
constructor($container, settings) {
this.$container = $container;
this.settings = settings;
this.$triggers = $container.find(settings.trigger);
this.$wrappers = $container.find(settings.contentWrapper);
this.hoverTimer = null;
this.currentIndex = -1;
this.$videos = null;
this.hasVideos = false;
if (!this.isValid()) {
console.warn('TabManager: Missing required elements');
return;
}
this.init();
}
isValid() {
return this.$triggers.length > 0 && this.$wrappers.length > 0;
}
init() {
// Check if this tab component has videos
this.detectVideos();
this.bindEvents();
if (this.hasVideos) {
this.initVideoProgression();
}
if (this.settings.isActiveFirstItem) {
this.activateTab(0);
}
}
detectVideos() {
// Check if container has video-tabs-wrap class or contains videos
const isVideoTabsWrap = this.$container.hasClass('video-tabs-wrap');
const $videoWrappers = this.$container.find(this.settings.videoWrapper);
const $videos = $videoWrappers.find(this.settings.videoSelector);
if ((isVideoTabsWrap || $videos.length > 0) && $videos.length > 0) {
this.hasVideos = true;
this.$videos = $videos;
}
}
initVideoProgression() {
if (!this.$videos || this.$videos.length === 0) return;
// Bind ended event to all videos
this.$videos.each((index, video) => {
$(video).on('ended', () => {
this.onVideoEnded(index);
});
});
}
onVideoEnded(videoIndex) {
// Calculate next index (loop back to 0 if at the end)
const nextIndex = (videoIndex + 1) % this.$triggers.length;
// Activate next tab
this.activateTab(nextIndex);
}
activateTab(index) {
if (index < 0 || index >= this.$triggers.length) return;
if (this.currentIndex === index) return;
const previousIndex = this.currentIndex;
this.currentIndex = index;
// Update triggers
this.$triggers
.removeClass(this.settings.activeClass)
.eq(index)
.addClass(this.settings.activeClass);
// Update content
const { speed, shownClass, content } = this.settings;
this.$wrappers.each((_, wrapper) => {
const $items = $(wrapper).find(content);
if (!$items.eq(index).length) return;
$items
.stop(true, true)
.hide()
.removeClass(shownClass);
$items
.eq(index)
.fadeIn(speed)
.addClass(shownClass);
});
// Handle video playback if videos exist
if (this.hasVideos) {
this.handleVideoPlayback(index, previousIndex);
}
}
handleVideoPlayback(currentIndex, previousIndex) {
if (!this.$videos) return;
// Pause previous video if exists
if (previousIndex >= 0 && previousIndex < this.$videos.length) {
const prevVideo = this.$videos.get(previousIndex);
if (prevVideo) {
prevVideo.pause();
prevVideo.currentTime = 0;
}
}
// Play current video
if (currentIndex >= 0 && currentIndex < this.$videos.length) {
const currentVideo = this.$videos.get(currentIndex);
if (currentVideo) {
currentVideo.currentTime = 0;
// Play with promise handling
const playPromise = currentVideo.play();
if (playPromise !== undefined) {
playPromise.catch(error => {
console.warn('Video autoplay prevented:', error);
});
}
}
}
}
bindEvents() {
const { eventType, hoverDelay } = this.settings;
this.$triggers.each((index, trigger) => {
const $trigger = $(trigger);
if (eventType === 'click') {
$trigger.on('click', (e) => {
e.preventDefault();
this.activateTab(index);
});
} else if (eventType === 'mouseenter') {
$trigger
.on('mouseenter', () => {
if (this.hoverTimer) clearTimeout(this.hoverTimer);
this.hoverTimer = setTimeout(() => this.activateTab(index), hoverDelay);
})
.on('mouseleave', () => {
if (this.hoverTimer) clearTimeout(this.hoverTimer);
});
}
});
}
}
/**
* Initialize tabs
*/
$.fn.initTabs = function(options) {
const settings = $.extend({}, DEFAULTS, options);
return this.each(function() {
new TabManager($(this), settings);
});
};
/**
* Auto-initialize on DOM ready
*/
$(function() {
// Click-based video tabs
$('.video-tabs-wrap').initTabs({
trigger: '.tab-nav',
contentWrapper: '.tabs-contents',
content: '.tab-content',
eventType: 'click',
speed: 400,
videoWrapper: '.video-wrapper',
videoSelector: '.elementor-video'
});
// Hover-based tabs
$('.e-parent').has('.tabs-nav-item').initTabs({
eventType: 'mouseenter',
speed: 350,
isActiveFirstItem: false
});
});
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment