[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-content/themes/twentytwentyone/assets/js/ -> primary-navigation.js (source)

   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.getElementById( 'site-navigation' ).addEventListener( 'click', function( event ) {
 176              // If target onclick is <a> with # within the href attribute
 177              if ( event.target.hash ) {
 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  }() );


Generated : Sat Dec 21 08:20:01 2024 Cross-referenced by PHPXref