1/** 2 * File primary-navigation.js. 3 * 4 * Required to open and close the mobile navigation. 5 */ 6 7/** 8 * Toggle an attribute's value 9 * 10 * @param {Element} el - The element. 11 * @param {boolean} withListeners - Whether we want to add/remove listeners or not. 12 * @since Twenty Twenty-One 1.0 13 */ 14function twentytwentyoneToggleAriaExpanded( el, withListeners ) { 15 if ( 'true' !== el.getAttribute( 'aria-expanded' ) ) { 16 el.setAttribute( 'aria-expanded', 'true' ); 17 twentytwentyoneSubmenuPosition( el.parentElement ); 18 if ( withListeners ) { 19 document.addEventListener( 'click', twentytwentyoneCollapseMenuOnClickOutside ); 20 } 21 } else { 22 el.setAttribute( 'aria-expanded', 'false' ); 23 if ( withListeners ) { 24 document.removeEventListener( 'click', twentytwentyoneCollapseMenuOnClickOutside ); 25 } 26 } 27} 28 29function twentytwentyoneCollapseMenuOnClickOutside( event ) { 30 if ( ! document.getElementById( 'site-navigation' ).contains( event.target ) ) { 31 document.getElementById( 'site-navigation' ).querySelectorAll( '.sub-menu-toggle' ).forEach( function( button ) { 32 button.setAttribute( 'aria-expanded', 'false' ); 33 } ); 34 } 35} 36 37/** 38 * Changes the position of submenus so they always fit the screen horizontally. 39 * 40 * @param {Element} li - The li element. 41 */ 42function twentytwentyoneSubmenuPosition( li ) { 43 var subMenu = li.querySelector( 'ul.sub-menu' ), 44 rect, 45 right, 46 left, 47 windowWidth; 48 49 if ( ! subMenu ) { 50 return; 51 } 52 53 rect = subMenu.getBoundingClientRect(); 54 right = Math.round( rect.right ); 55 left = Math.round( rect.left ); 56 windowWidth = Math.round( window.innerWidth ); 57 58 if ( right > windowWidth ) { 59 subMenu.classList.add( 'submenu-reposition-right' ); 60 } else if ( document.body.classList.contains( 'rtl' ) && left < 0 ) { 61 subMenu.classList.add( 'submenu-reposition-left' ); 62 } 63} 64 65/** 66 * Handle clicks on submenu toggles. 67 * 68 * @param {Element} el - The element. 69 */ 70function twentytwentyoneExpandSubMenu( el ) { // jshint ignore:line 71 // Close other expanded items. 72 el.closest( 'nav' ).querySelectorAll( '.sub-menu-toggle' ).forEach( function( button ) { 73 if ( button !== el ) { 74 button.setAttribute( 'aria-expanded', 'false' ); 75 } 76 } ); 77 78 // Toggle aria-expanded on the button. 79 twentytwentyoneToggleAriaExpanded( el, true ); 80 81 // On tab-away collapse the menu. 82 el.parentNode.querySelectorAll( 'ul > li:last-child > a' ).forEach( function( linkEl ) { 83 linkEl.addEventListener( 'blur', function( event ) { 84 if ( ! el.parentNode.contains( event.relatedTarget ) ) { 85 el.setAttribute( 'aria-expanded', 'false' ); 86 } 87 } ); 88 } ); 89} 90 91( function() { 92 /** 93 * Menu Toggle Behaviors 94 * 95 * @param {string} id - The ID. 96 */ 97 var navMenu = function( id ) { 98 var wrapper = document.body, // this is the element to which a CSS class is added when a mobile nav menu is open 99 mobileButton = document.getElementById( id + '-mobile-menu' ); 100 101 if ( mobileButton ) { 102 mobileButton.onclick = function() { 103 wrapper.classList.toggle( id + '-navigation-open' ); 104 wrapper.classList.toggle( 'lock-scrolling' ); 105 twentytwentyoneToggleAriaExpanded( mobileButton ); 106 mobileButton.focus(); 107 }; 108 } 109 /** 110 * Trap keyboard navigation in the menu modal. 111 * Adapted from TwentyTwenty 112 */ 113 document.addEventListener( 'keydown', function( event ) { 114 var modal, elements, selectors, lastEl, firstEl, activeEl, tabKey, shift
114Key, escKey; 115 if ( ! wrapper.classList.contains( id + '-navigation-open' ) ) { 116 return; 117 } 118 119 modal = document.querySelector( '.' + id + '-navigation' ); 120 selectors = 'input, a, button'; 121 elements = modal.querySelectorAll( selectors ); 122 elements = Array.prototype.slice.call( elements ); 123 tabKey = event.keyCode === 9; 124 shiftKey = event.shiftKey; 125 escKey = event.keyCode === 27; 126 activeEl = document.activeElement; // eslint-disable-line @wordpress/no-global-active-element 127 lastEl = elements[ elements.length - 1 ]; 128 firstEl = elements[0]; 129 130 if ( escKey ) { 131 event.preventDefault(); 132 wrapper.classList.remove( id + '-navigation-open', 'lock-scrolling' ); 133 twentytwentyoneToggleAriaExpanded( mobileButton ); 134 mobileButton.focus(); 135 } 136 137 if ( ! shiftKey && tabKey && lastEl === activeEl ) { 138 event.preventDefault(); 139 firstEl.focus(); 140 } 141 142 if ( shiftKey && tabKey && firstEl === activeEl ) { 143 event.preventDefault(); 144 lastEl.focus(); 145 } 146 147 // If there are no elements in the menu, don't move the focus 148 if ( tabKey && firstEl === lastEl ) { 149 event.preventDefault(); 150 } 151 } ); 152 153 /** 154 * Close menu and scroll to anchor when an anchor link is clicked. 155 * Adapted from TwentyTwenty. 156 */ 157 document.addEventListener( 'click', function( event ) { 158 // If target onclick is <a> with # within the href attribute 159 if ( event.target.hash && event.target.hash.includes( '#' ) ) { 160 wrapper.classList.remove( id + '-navigation-open', 'lock-scrolling' ); 161 twentytwentyoneToggleAriaExpanded( mobileButton ); 162 // Wait 550 and scroll to the anchor. 163 setTimeout(function () { 164 var anchor = document.getElementById(event.target.hash.slice(1)); 165 anchor.scrollIntoView(); 166 }, 550); 167 } 168 } ); 169 170 document.getElementById( 'site-navigation' ).querySelectorAll( '.menu-wrapper > .menu-item-has-children' ).forEach( function( li ) { 171 li.addEventListener( 'mouseenter', function() { 172 this.querySelector( '.sub-menu-toggle' ).setAttribute( 'aria-expanded', 'true' ); 173 twentytwentyoneSubmenuPosition( li ); 174 } ); 175 li.addEventListener( 'mouseleave', function() { 176 this.querySelector( '.sub-menu-toggle' ).setAttribute( 'aria-expanded', 'false' ); 177 } ); 178 } ); 179 }; 180 181 window.addEventListener( 'load', function() { 182 new navMenu( 'primary' ); 183 } ); 184}() );
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.