balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

primary-navigation.js (6018B)


      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 					anchor.scrollIntoView();
    184 				}, 550);
    185 			}
    186 		} );
    187 
    188 		navMenuEl.querySelectorAll( '.menu-wrapper > .menu-item-has-children' ).forEach( function( li ) {
    189 			li.addEventListener( 'mouseenter', function() {
    190 				this.querySelector( '.sub-menu-toggle' ).setAttribute( 'aria-expanded', 'true' );
    191 				twentytwentyoneSubmenuPosition( li );
    192 			} );
    193 			li.addEventListener( 'mouseleave', function() {
    194 				this.querySelector( '.sub-menu-toggle' ).setAttribute( 'aria-expanded', 'false' );
    195 			} );
    196 		} );
    197 	};
    198 
    199 	window.addEventListener( 'load', function() {
    200 		new navMenu( 'primary' );
    201 	} );
    202 }() );