| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- /**
- * File primary-navigation.js.
- *
- * Required to open and close the mobile navigation.
- */
- /**
- * Toggle an attribute's value
- *
- * @since Twenty Twenty-One 1.0
- *
- * @param {Element} el - The element.
- * @param {boolean} withListeners - Whether we want to add/remove listeners or not.
- */
- function twentytwentyoneToggleAriaExpanded( el, withListeners ) {
- if ( 'true' !== el.getAttribute( 'aria-expanded' ) ) {
- el.setAttribute( 'aria-expanded', 'true' );
- twentytwentyoneSubmenuPosition( el.parentElement );
- if ( withListeners ) {
- document.addEventListener( 'click', twentytwentyoneCollapseMenuOnClickOutside );
- }
- } else {
- el.setAttribute( 'aria-expanded', 'false' );
- if ( withListeners ) {
- document.removeEventListener( 'click', twentytwentyoneCollapseMenuOnClickOutside );
- }
- }
- }
- function twentytwentyoneCollapseMenuOnClickOutside( event ) {
- if ( ! document.getElementById( 'site-navigation' ).contains( event.target ) ) {
- document.getElementById( 'site-navigation' ).querySelectorAll( '.sub-menu-toggle' ).forEach( function( button ) {
- button.setAttribute( 'aria-expanded', 'false' );
- } );
- }
- }
- /**
- * Changes the position of submenus so they always fit the screen horizontally.
- *
- * @since Twenty Twenty-One 1.0
- *
- * @param {Element} li - The li element.
- */
- function twentytwentyoneSubmenuPosition( li ) {
- var subMenu = li.querySelector( 'ul.sub-menu' ),
- rect,
- right,
- left,
- windowWidth;
- if ( ! subMenu ) {
- return;
- }
- rect = subMenu.getBoundingClientRect();
- right = Math.round( rect.right );
- left = Math.round( rect.left );
- windowWidth = Math.round( window.innerWidth );
- if ( right > windowWidth ) {
- subMenu.classList.add( 'submenu-reposition-right' );
- } else if ( document.body.classList.contains( 'rtl' ) && left < 0 ) {
- subMenu.classList.add( 'submenu-reposition-left' );
- }
- }
- /**
- * Handle clicks on submenu toggles.
- *
- * @since Twenty Twenty-One 1.0
- *
- * @param {Element} el - The element.
- */
- function twentytwentyoneExpandSubMenu( el ) { // jshint ignore:line
- // Close other expanded items.
- el.closest( 'nav' ).querySelectorAll( '.sub-menu-toggle' ).forEach( function( button ) {
- if ( button !== el ) {
- button.setAttribute( 'aria-expanded', 'false' );
- }
- } );
- // Toggle aria-expanded on the button.
- twentytwentyoneToggleAriaExpanded( el, true );
- // On tab-away collapse the menu.
- el.parentNode.querySelectorAll( 'ul > li:last-child > a' ).forEach( function( linkEl ) {
- linkEl.addEventListener( 'blur', function( event ) {
- if ( ! el.parentNode.contains( event.relatedTarget ) ) {
- el.setAttribute( 'aria-expanded', 'false' );
- }
- } );
- } );
- }
- ( function() {
- /**
- * Menu Toggle Behaviors
- *
- * @since Twenty Twenty-One 1.0
- *
- * @param {string} id - The ID.
- */
- var navMenu = function( id ) {
- var wrapper = document.body, // this is the element to which a CSS class is added when a mobile nav menu is open
- mobileButton = document.getElementById( id + '-mobile-menu' ),
- navMenuEl = document.getElementById( 'site-navigation' );
- // If there's no nav menu, none of this is necessary.
- if ( ! navMenuEl ) {
- return;
- }
- if ( mobileButton ) {
- mobileButton.onclick = function() {
- wrapper.classList.toggle( id + '-navigation-open' );
- wrapper.classList.toggle( 'lock-scrolling' );
- twentytwentyoneToggleAriaExpanded( mobileButton );
- mobileButton.focus();
- };
- }
- /**
- * Trap keyboard navigation in the menu modal.
- * Adapted from Twenty Twenty.
- *
- * @since Twenty Twenty-One 1.0
- */
- document.addEventListener( 'keydown', function( event ) {
- var modal, elements, selectors, lastEl, firstEl, activeEl, tabKey, shiftKey, escKey;
- if ( ! wrapper.classList.contains( id + '-navigation-open' ) ) {
- return;
- }
- modal = document.querySelector( '.' + id + '-navigation' );
- selectors = 'input, a, button';
- elements = modal.querySelectorAll( selectors );
- elements = Array.prototype.slice.call( elements );
- tabKey = event.keyCode === 9;
- shiftKey = event.shiftKey;
- escKey = event.keyCode === 27;
- activeEl = document.activeElement; // eslint-disable-line @wordpress/no-global-active-element
- lastEl = elements[ elements.length - 1 ];
- firstEl = elements[0];
- if ( escKey ) {
- event.preventDefault();
- wrapper.classList.remove( id + '-navigation-open', 'lock-scrolling' );
- twentytwentyoneToggleAriaExpanded( mobileButton );
- mobileButton.focus();
- }
- if ( ! shiftKey && tabKey && lastEl === activeEl ) {
- event.preventDefault();
- firstEl.focus();
- }
- if ( shiftKey && tabKey && firstEl === activeEl ) {
- event.preventDefault();
- lastEl.focus();
- }
- // If there are no elements in the menu, don't move the focus
- if ( tabKey && firstEl === lastEl ) {
- event.preventDefault();
- }
- } );
- /**
- * Close menu and scroll to anchor when an anchor link is clicked.
- * Adapted from Twenty Twenty.
- *
- * @since Twenty Twenty-One 1.1
- */
- document.addEventListener( 'click', function( event ) {
- // If target onclick is <a> with # within the href attribute
- if ( event.target.hash && event.target.hash.includes( '#' ) ) {
- wrapper.classList.remove( id + '-navigation-open', 'lock-scrolling' );
- twentytwentyoneToggleAriaExpanded( mobileButton );
- // Wait 550 and scroll to the anchor.
- setTimeout(function () {
- var anchor = document.getElementById(event.target.hash.slice(1));
- anchor.scrollIntoView();
- }, 550);
- }
- } );
- navMenuEl.querySelectorAll( '.menu-wrapper > .menu-item-has-children' ).forEach( function( li ) {
- li.addEventListener( 'mouseenter', function() {
- this.querySelector( '.sub-menu-toggle' ).setAttribute( 'aria-expanded', 'true' );
- twentytwentyoneSubmenuPosition( li );
- } );
- li.addEventListener( 'mouseleave', function() {
- this.querySelector( '.sub-menu-toggle' ).setAttribute( 'aria-expanded', 'false' );
- } );
- } );
- };
- window.addEventListener( 'load', function() {
- new navMenu( 'primary' );
- } );
- }() );
|