1// File#: _3_hiding-nav 2// Usage: codyhouse.co/license 3(function() { 4 var hidingNav = document.getElementsByClassName('js-hide-nav'); 5 if(hidingNav.length > 0 && window.requestAnimationFrame) { 6 var mainNav = Array.prototype.filter.call(hidingNav, function(element) { 7 return element.classList.contains('js-hide-nav--main'); 8 }), 9 subNav = Array.prototype.filter.call(hidingNav, function(element) { 10 return element.classList.contains('js-hide-nav--sub'); 11 }); 12 13 var scrolling = false, 14 previousTop = window.scrollY, 15 currentTop = window.scrollY, 16 scrollDelta = 10, 17 scrollOffset = 150, // scrollY needs to be bigger than scrollOffset to hide navigation 18 headerHeight = 0; 19 20 var navIsFixed = false; // check if main navigation is fixed 21 if(mainNav.length > 0 && mainNav[0].classList.contains('hide-nav--fixed')) navIsFixed = true; 22 23 // store button that triggers navigation on mobile 24 var triggerMobile = getTriggerMobileMenu(); 25 var prevElement = createPrevElement(); 26 var mainNavTop = 0; 27 // list of classes the hide-nav has when it is expanded -> do not hide if it has those classes 28 var navOpenClasses = hidingNav[0].getAttribute('data-nav-target-class'), 29 navOpenArrayClasses = []; 30 if(navOpenClasses) navOpenArrayClasses = navOpenClasses.split(' '); 31 getMainNavTop(); 32 if(mainNavTop > 0) { 33 scrollOffset = scrollOffset + mainNavTop; 34 } 35 36 // init navigation and listen to window scroll event 37 getHeaderHeight(); 38 initSecondaryNav(); 39 initFixedNav(); 40 resetHideNav(); 41 window.addEventListener('scroll', function(event){ 42 if(scrolling) return; 43 scrolling = true; 44 window.requestAnimationFrame(resetHideNav); 45 }); 46 47 window.addEventListener('resize', function(event){ 48 if(scrolling) return; 49 scrolling = true; 50 window.requestAnimationFrame(function(){ 51 if(headerHeight > 0) { 52 getMainNavTop(); 53 getHeaderHeight(); 54 initSecondaryNav(); 55 initFixedNav(); 56 } 57 // reset both navigation 58 hideNavScrollUp(); 59 60 scrolling = false; 61 }); 62 }); 63 64 function getHeaderHeight() { 65 headerHeight = mainNav[0].offsetHeight; 66 }; 67 68 function initSecondaryNav() { // if there's a secondary nav, set its top equal to the header height 69 if(subNav.length < 1 || mainNav.length < 1) return; 70 subNav[0].style.top = (headerHeight - 1)+'px'; 71 }; 72 73 function initFixedNav() { 74 if(!navIsFixed || mainNav.length < 1) return; 75 mainNav[0].style.marginBottom = '-'+headerHeight+'px'; 76 }; 77 78 function resetHideNav() { // check if navs need to be hidden/revealed 79 currentTop = window.scrollY; 80 if(currentTop - previousTop > scrollDelta && currentTop > scrollOffset) { 81 hideNavScrollDown(); 82 } else if( previousTop - currentTop > scrollDelta || (previousTop - currentTop > 0 && currentTop < scrollOffset) ) { 83 hideNavScrollUp(); 84 } else if( previousTop - currentTop > 0 && subNav.length > 0 && subNav[0].getBoundingClientRect().top > 0) { 85 setTranslate(subNav[0], '0%'); 86 } 87 // if primary nav is fixed -> toggle bg class 88 if(navIsFixed) { 89 var scrollTop = window.scrollY || window.pageYOffset; 90 mainNav[0].classList.toggle('hide-nav--has-bg', (scrollTop > headerHeight + mainNavTop)); 91 } 92 previousTop = currentTop; 93 scrolling = false; 94 }; 95 96 function hideNavScrollDown() { 97 // if there's a secondary nav -> it has to reach the top before hiding nav 98 if( subNav.length > 0 && subNav[0].getBoundingClientRect().top > headerHeight) return; 99 // on mobile -> hide navigation only if dropdown is not open 100 if(triggerMobile && triggerMobile.getAttribute('aria-expanded') == "true") return; 101 // check if main nav has one of the following classes 102 if( mainNav.length > 0 && (!navOpenClasses || !checkNavExpanded())) { 103 setTranslate(mainNav[0], '-100%'); 104 mainNav[0].addEventListener('transitionend', addOffCanvasClass); 105 } 106 if( subNav.length > 0 ) setTranslate(subNav[0], '-'+headerHeight+'px'); 107 }; 108 109 function hideNavScrollUp() { 110 if( mainNav.length > 0 ) {setTranslate(mainNav[0], '0%'); mainNav[0].classList.remove('hide-nav--off-canvas');mainNav[0].removeEventListener('transitionend', addOffCanvasClass);} 111 if( subNav.length > 0 ) setTranslate(subNav[0], '0%'); 112 }; 113 114 function addOffCanvasClass() { 115 mainNav[0].removeEventListener('transitionend', addOffCanvasClass); 116 mainNav[0].classList.add('hide-nav--off-canvas'); 117 }; 118 119 function setTranslate(element, val) { 120 element.style.transform = 'translateY('+val+')'; 121 }; 122 123 function getTriggerMobileMenu() { 124 // store trigger that toggle mobile navigation dropdown 125 var triggerMobileClass = hidingNav[0].getAttribute('data-mobile-trigger'); 126 if(!triggerMobileClass) return false;
127 if(triggerMobileClass.indexOf('#') == 0) { // get trigger by ID 128 var trigger = document.getElementById(triggerMobileClass.replace('#', '')); 129 if(trigger) return trigger; 130 } else { // get trigger by class name 131 var trigger = hidingNav[0].getElementsByClassName(triggerMobileClass); 132 if(trigger.length > 0) return trigger[0]; 133 } 134 135 return false; 136 }; 137 138 function createPrevElement() { 139 // create element to be inserted right before the mainNav to get its top value 140 if( mainNav.length < 1) return false; 141 var newElement = document.createElement("div"); 142 newElement.setAttribute('aria-hidden', 'true'); 143 mainNav[0].parentElement.insertBefore(newElement, mainNav[0]); 144 var prevElement = mainNav[0].previousElementSibling; 145 prevElement.style.opacity = '0'; 146 return prevElement; 147 }; 148 149 function getMainNavTop() { 150 if(!prevElement) return; 151 mainNavTop = prevElement.getBoundingClientRect().top + window.scrollY; 152 }; 153 154 function checkNavExpanded() { 155 var navIsOpen = false; 156 for(var i = 0; i < navOpenArrayClasses.length; i++){ 157 if(mainNav[0].classList.contains(navOpenArrayClasses[i].trim())) { 158 navIsOpen = true; 159 break; 160 } 161 } 162 return navIsOpen; 163 }; 164 165 } else { 166 // if window requestAnimationFrame is not supported -> add bg class to fixed header 167 var mainNav = document.getElementsByClassName('js-hide-nav--main'); 168 if(mainNav.length < 1) return; 169 if(mainNav[0].classList.contains('hide-nav--fixed')) mainNav[0].classList.add('hide-nav--has-bg'); 170 } 171}());
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.