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