PageSourceSearch

https://futureupodcast.com/static/js/components/_1_looping-tabs.js

js futureupodcast.com collected 2026-10-02 05:05:22 UTC 7,011 bytes, 170 lines download raw bytes

1// File#: _1_looping_tabs
2// Usage: codyhouse.co/license
3(function() { 
4  var LoopTab = function(opts) {
5    this.options = Util.extend(LoopTab.defaults , opts);
6		this.element = this.options.element;
7		this.tabList = this.element.getElementsByClassName('js-loop-tabs__controls')[0];
8		this.listItems = this.tabList.getElementsByTagName('li');
9		this.triggers = this.tabList.getElementsByTagName('a');
10		this.panelsList = this.element.getElementsByClassName('js-loop-tabs__panels')[0];
11    this.panels = Util.getChildrenByClassName(this.panelsList, 'js-loop-tabs__panel');
12    this.assetsList = this.element.getElementsByClassName('js-loop-tabs__assets')[0];
13		this.assets = this.assetsList.getElementsByTagName('li');
14    this.panelShowClass = 'loop-tabs__panel--selected';
15		this.assetShowClass = 'loop-tabs__asset--selected';
16		this.assetExitClass = 'loop-tabs__asset--exit';
17    this.controlActiveClass = 'loop-tabs__control--selected';
18    // autoplay
19    this.autoplayPaused = false;
20		this.loopTabAutoId = false;
21		this.loopFillAutoId = false;
22		this.loopFill = 0;
23		initLoopTab(this);
24  };
25  
26  function initLoopTab(tab) {
27    //set initial aria attributes
28		tab.tabList.setAttribute('role', 'tablist');
29		for( var i = 0; i < tab.triggers.length; i++) {
30			var bool = Util.hasClass(tab.triggers[i], tab.controlActiveClass),
31        panelId = tab.panels[i].getAttribute('id');
32			tab.listItems[i].setAttribute('role', 'presentation');
33			Util.setAttributes(tab.triggers[i], {'role': 'tab', 'aria-selected': bool, 'aria-controls': panelId, 'id': 'tab-'+panelId});
34			Util.addClass(tab.triggers[i], 'js-loop-tabs__trigger'); 
35      Util.setAttributes(tab.panels[i], {'role': 'tabpanel', 'aria-labelledby': 'tab-'+panelId});
36      Util.toggleClass(tab.panels[i], tab.panelShowClass, bool);
37      Util.toggleClass(tab.assets[i], tab.assetShowClass, bool);
38
39			if(!bool) tab.triggers[i].setAttribute('tabindex', '-1'); 
40		}
41		// add autoplay-off class if needed
42		!tab.options.autoplay && Util.addClass(tab.element, 'loop-tabs--autoplay-off');
43		//listen for Tab events
44		initLoopTabEvents(tab);
45  };
46
47  function initLoopTabEvents(tab) {
48		if(tab.options.autoplay) { 
49			initLoopTabAutoplay(tab); // init autoplay
50			// pause autoplay if user is interacting with the tabs
51			tab.element.addEventListener('focusin', function(event){
52				pauseLoopTabAutoplay(tab);
53				tab.autoplayPaused = true;
54			});
55			tab.element.addEventListener('focusout', function(event){
56				tab.autoplayPaused = false;
57				initLoopTabAutoplay(tab);
58			});
59		}
60
61    //click on a new tab -> select content
62		tab.tabList.addEventListener('click', function(event) {
63			if( event.target.closest('.js-loop-tabs__trigger') ) triggerLoopTab(tab, event.target.closest('.js-loop-tabs__trigger'), event);
64		});
65		
66    //arrow keys to navigate through tabs 
67		tab.tabList.addEventListener('keydown', function(event) {
68			if( !event.target.closest('.js-loop-tabs__trigger') ) return;
69			if( event.keyCode && event.keyCode == 39 || event.key && event.key.toLowerCase() == 'arrowright' ) {
70				pauseLoopTabAutoplay(tab);
71				selectNewLoopTab(tab, 'next', true);
72			} else if( event.keyCode && event.keyCode == 37 || event.key && event.key.toLowerCase() == 'arrowleft' ) {
73				pauseLoopTabAutoplay(tab);
74				selectNewLoopTab(tab, 'prev', true);
75			}
76		});
77  };
78
79  function initLoopTabAutoplay(tab) {
80		if(!tab.options.autoplay || tab.autoplayPaused) return;
81		tab.loopFill = 0;
82		var selectedTab = tab.tabList.getElementsByClassName(tab.controlActiveClass)[0];
83		// reset css variables
84		for(var i = 0; i < tab.triggers.length; i++) {
85			if(cssVariableSupport) tab.triggers[i].style.setProperty('--loop-tabs-filling', 0);
86		}
87		
88		tab.loopTabAutoId = setTimeout(function(){
89      selectNewLoopTab(tab, 'next', false);
90		}, tab.options.autoplayInterval);
91		
92		if(cssVariableSupport) { // tab fill effect
93			tab.loopFillAutoId = setInterval(function(){
94				tab.loopFill = tab.loopFill + 0.005;
95				selectedTab.style.setProperty('--loop-tabs-filling', tab.loopFill);
96			}, tab.options.autoplayInterval/200);
97		}
98  };
99
100  function pauseLoopTabAutoplay(tab) { // pause autoplay
101    if(tab.loopTabAutoId) {
102			clearTimeout(tab.loopTabAutoId);
103			tab.loopTabAutoId = false;
104			clearInterval(tab.loopFillAutoId);
105			tab.loopFillAutoId = false;
106		}
107  };
108
109  function selectNewLoopTab(tab, direction, bool) {
110    var selectedTab = tab.tabList.getElementsByClassName(tab.controlActiveClass)[0],
111			index = Util.getIndexInArray(tab.triggers, selectedTab);
112		index = (direction == 'next') ? index + 1 : index - 1;
113		//make sure index is in the correct interval 
114		//-> from last element go to first using the right arrow, from first element go to last using the left arrow
115		if(index < 0) index = tab.listItems.length - 1;
116		if(index >= tab.listItems.length) index = 0;	
117		triggerLoopTab(tab, tab.triggers[index]);
118		bool && tab.triggers[index].focus();
119  };
120
121  function triggerLoopTab(tab, tabTrigger, event) {
122		pauseLoopTabAutoplay(tab);
123		event && event.preventDefault();	
124		var index = Util.getIndexInArray(tab.triggers, tabTrigger);
125		//no need to do anything if tab was already selected
126		if(Util.hasClass(tab.triggers[index], tab.controlActiveClass)) return;
127		
128		for( var i = 0; i < tab.triggers.length; i++) {
129			var bool = (i == index),
130				exit = Util.hasClass(tab.triggers[i], tab.controlActiveClass);
131			Util.toggleClass(tab.triggers[i], tab.controlActiveClass, bool);
132      Util.toggleClass(tab.panels[i], tab.panelShowClass, bool);
133			Util.toggleClass(tab.assets[i], tab.assetShowClass, bool);
134			Util.toggleClass(tab.assets[i], tab.assetExitClass, exit);
135			tab.triggers[i].setAttribute('aria-selected', bool);
136			bool ? tab.triggers[i].setAttribute('tabindex', '0') : tab.triggers[i].setAttribute('tabindex', '-1');
137
138			// listen for the end of animation on asset element and remove exit class
139			if(exit) {(function(i){
140				tab.assets[i].addEventListener('transitionend', function cb(event){
141					tab.assets[i].removeEventListener('transitionend', cb);
142					Util.removeClass(tab.assets[i], tab.assetExitClass);
143				});
144			})(i);}
145    }
146    
147    // restart tab autoplay
148    initLoopTabAutoplay(tab);
149  };
150
151  LoopTab.defaults = {
152    element : '',
153    autoplay : true,
154    autoplayInterval: 5000
155  };
156
157  //initialize the Tab objects
158	var loopTabs = document.getElementsByClassName('js-loop-tabs');
159	if( loopTabs.length > 0 ) {
160		var reducedMotion = Util.osHasReducedMotion(),
161			cssVariableSupport = ('CSS' in window) && Util.cssSupports('color', 'var(--var)');
162		for( var i = 0; i < loopTabs.length; i++) {
163			(function(i){
164        var autoplay = (loopTabs[i].getAttribute('data-autoplay') && loopTabs[i].getAttribute('data-autoplay') == 'off' || reducedMotion) ? false : true,
165        autoplayInterval = (loopTabs[i].getAttribute('data-autoplay-interval')) ? loopTabs[i].getAttribute('data-autoplay-interval') : 5000;
166        new LoopTab({element: loopTabs[i], autoplay : autoplay, autoplayInterval : autoplayInterval});
167      })(i);
168		}
169	}
170}());

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.