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.