1// File#: _1_tabs 2// Usage: codyhouse.co/license 3(function() { 4 var Tab = function(element) { 5 this.element = element; 6 this.tabList = this.element.getElementsByClassName('js-tabs__controls')[0]; 7 this.listItems = this.tabList.getElementsByTagName('li'); 8 this.triggers = this.tabList.getElementsByTagName('a'); 9 this.panelsList = this.element.getElementsByClassName('js-tabs__panels')[0]; 10 this.panels = Util.getChildrenByClassName(this.panelsList, 'js-tabs__panel'); 11 this.hideClass = 'is-hidden'; 12 this.customShowClass = this.element.getAttribute('data-show-panel-class') ? this.element.getAttribute('data-show-panel-class') : false; 13 this.layout = this.element.getAttribute('data-tabs-layout') ? this.element.getAttribute('data-tabs-layout') : 'horizontal'; 14 // deep linking options 15 this.deepLinkOn = this.element.getAttribute('data-deep-link') == 'on'; 16 // init tabs 17 this.initTab(); 18 }; 19 20 Tab.prototype.initTab = function() { 21 //set initial aria attributes 22 this.tabList.setAttribute('role', 'tablist'); 23 for( var i = 0; i < this.triggers.length; i++) { 24 var bool = (i == 0), 25 panelId = this.panels[i].getAttribute('id'); 26 this.listItems[i].setAttribute('role', 'presentation'); 27 Util.setAttributes(this.triggers[i], {'role': 'tab', 'aria-selected': bool, 'aria-controls': panelId, 'id': 'tab-'+panelId}); 28 Util.addClass(this.triggers[i], 'js-tabs__trigger'); 29 Util.setAttributes(this.panels[i], {'role': 'tabpanel', 'aria-labelledby': 'tab-'+panelId}); 30 Util.toggleClass(this.panels[i], this.hideClass, !bool); 31 32 if(!bool) this.triggers[i].setAttribute('tabindex', '-1'); 33 } 34 35 //listen for Tab events 36 this.initTabEvents(); 37 38 // check deep linking option 39 this.initDeepLink(); 40 }; 41 42 Tab.prototype.initTabEvents = function() { 43 var self = this; 44 //click on a new tab -> select content 45 this.tabList.addEventListener('click', function(event) { 46 if( event.target.closest('.js-tabs__trigger') ) self.triggerTab(event.target.closest('.js-tabs__trigger'), event); 47 }); 48 //arrow keys to navigate through tabs 49 this.tabList.addEventListener('keydown', function(event) { 50 ; 51 if( !event.target.closest('.js-tabs__trigger') ) return; 52 if( tabNavigateNext(event, self.layout) ) { 53 event.preventDefault(); 54 self.selectNewTab('next'); 55 } else if( tabNavigatePrev(event, self.layout) ) { 56 event.preventDefault(); 57 self.selectNewTab('prev'); 58 } 59 }); 60 }; 61 62 Tab.prototype.selectNewTab = function(direction) { 63 var selectedTab = this.tabList.querySelector('[aria-selected="true"]'), 64 index = Util.getIndexInArray(this.triggers, selectedTab); 65 index = (direction == 'next') ? index + 1 : index - 1; 66 //make sure index is in the correct interval 67 //-> from last element go to first using the right arrow, from first element go to last using the left arrow 68 if(index < 0) index = this.listItems.length - 1; 69 if(index >= this.listItems.length) index = 0; 70 this.triggerTab(this.triggers[index]); 71 this.triggers[index].focus(); 72 }; 73 74 Tab.prototype.triggerTab = function(tabTrigger, event) { 75 var self = this; 76 event && event.preventDefault(); 77 var index = Util.getIndexInArray(this.triggers, tabTrigger); 78 //no need to do anything if tab was already selected 79 if(this.triggers[index].getAttribute('aria-selected') == 'true') return; 80 81 for( var i = 0; i < this.triggers.length; i++) { 82 var bool = (i == index); 83 Util.toggleClass(this.panels[i], this.hideClass, !bool); 84 if(this.customShowClass) Util.toggleClass(this.panels[i], this.customShowClass, bool); 85 this.triggers[i].setAttribute('aria-selected', bool); 86 bool ? this.triggers[i].setAttribute('tabindex', '0') : this.triggers[i].setAttribute('tabindex', '-1'); 87 } 88 89 // update url if deepLink is on 90 if(this.deepLinkOn) { 91 history.replaceState(null, '', '#'+tabTrigger.getAttribute('aria-controls')); 92 } 93 }; 94 95 Tab.prototype.initDeepLink = function() { 96 if(!this.deepLinkOn) return; 97 var hash = window.location.hash.substr(1); 98 var self = this; 99 if(!hash || hash == '') return; 100 for(var i = 0; i < this.panels.length; i++) { 101 if(this.panels[i].getAttribute('id') == hash) { 102 this.triggerTab(this.triggers[i], false); 103 setTimeout(function(){self.panels[i].scrollIntoView(true);}); 104 break; 105 } 106 }; 107 }; 108 109 function tabNavigateNext(event, layout) { 110 if(layout == 'horizontal' && (event.keyCode && event.keyCode == 39 || event.key && event.key == 'ArrowRight')) {return true;} 111 else if(layout == 'vertical' && (event.keyCode && event.keyCode == 40 || event.key && event.key == 'ArrowDown')) {return true;} 112 else {return false;} 113 }; 114 115 function tabNavigatePrev(event, layout) { 116 if(layout == 'horizontal' && (event.keyCode && event.keyCode == 37 || event.key && event.key == 'ArrowLeft')) {return true;} 117 else if(layout == 'vertical' && (event.keyCode && event.keyCode == 38 || event.key && event.key == 'ArrowUp')) {return true;} 118 else {return false;} 119 }; 120 121 //initialize the Tab objects 122 var tabs = document.getElementsByClassName('js-tabs'); 123 if( tabs.length > 0 ) { 124 for( var i = 0; i < tabs.length; i++) { 125 (function(i){new Tab(tabs[i]);})(i); 126 } 127 } 128 }());
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.