Açıklama Yok

primary-navigation.js 5.9KB

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