navigation.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /* global textBookScreenReaderText, textBookMenuToggleText */
  2. ( function( $ ) {
  3. var body, masthead, menuToggle, siteMenu, siteNavigation;
  4. masthead = $( '#masthead' );
  5. siteMenu = masthead.find( '.main-navigation' );
  6. menuToggle = masthead.find( '.menu-toggle' );
  7. siteNavigation = masthead.find( '.main-navigation > div' );
  8. function initMainNavigation( container ) {
  9. // Create dropdown toggle that displays child menu items.
  10. var dropdownToggle = $( '<button />', {
  11. 'class': 'dropdown-toggle',
  12. 'aria-expanded': false,
  13. 'tabindex': -1
  14. } ).append( $( '<span />', {
  15. 'class': 'screen-reader-text',
  16. text: textBookScreenReaderText.expand
  17. } ) ).append( $( '<svg x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" xml:space="preserve"><polygon points="13,7 9,7 9,3 7,3 7,7 3,7 3,9 7,9 7,13 9,13 9,9 13,9 "/></svg>' ) );
  18. // Add dropdown toggle to menu items with children.
  19. container.find( '.menu-item-has-children > a, .page_item_has_children > a' ).after( dropdownToggle );
  20. // Toggle buttons and submenu items with active children menu items.
  21. container.find( '.current-menu-ancestor > button, .current_page_ancestor > button' ).addClass( 'toggled-on' );
  22. container.find( '.current-menu-ancestor > ul, .current_page_ancestor > ul' ).addClass( 'toggled-on' );
  23. // Add aria-haspopup="true" to menu items with submenus.
  24. container.find( '.menu-item-has-children > a, .page_item_has_children > a' ).attr( 'aria-haspopup', 'true' );
  25. container.find( '.menu-item-has-children > ul, .page_item_has_children > ul' ).attr( 'aria-hidden', 'true' );
  26. // Click actions for dropdown toggle.
  27. container.find( '.dropdown-toggle' ).click( function( e ) {
  28. var _this = $( this ),
  29. textBookScreenReaderSpan = _this.find( '.screen-reader-text' );
  30. // Disable default click actions.
  31. e.preventDefault();
  32. // Make sure submenus get collapsed when parent submenus are collapsed.
  33. if ( _this.hasClass( 'toggled-on' ) && _this.next( '.children, .sub-menu' ).hasClass( 'toggled-on' ) ) {
  34. _this.removeClass( 'toggled-on' );
  35. _this.next( '.children, .sub-menu' ).removeClass( 'toggled-on' )
  36. .find( '.children, .sub-menu, .dropdown-toggle' ).removeClass( 'toggled-on' );
  37. } else {
  38. _this.addClass( 'toggled-on' );
  39. _this.next( '.children, .sub-menu, .children .children, .sub-menu .sub-menu' ).addClass( 'toggled-on' );
  40. }
  41. // jscs:disable
  42. _this.attr( 'aria-expanded', _this.attr( 'aria-expanded' ) === 'false' ? 'true' : 'false' );
  43. // jscs:enable
  44. // Update screen reader text.
  45. textBookScreenReaderSpan.text( textBookScreenReaderSpan.text() === textBookScreenReaderText.expand ? textBookScreenReaderText.collapse : textBookScreenReaderText.expand );
  46. } );
  47. }
  48. initMainNavigation( siteMenu );
  49. /*
  50. * Add separators to split menu items horizontally
  51. */
  52. function addNavigationSeparators( container ) {
  53. // Count how many top-level menus exist.
  54. var siteMenuItemCount = container.children( 'div' ).eq( 0 ).find( '> ul > .menu-item, > ul > .page_item' ).length;
  55. if ( siteMenuItemCount >= 6 ) { // If there are more than 5 menu items, add visual separators to menus.
  56. container.addClass( 'add-seperators' );
  57. } else { // otherwise, remove visual separators to menus.
  58. container.removeClass( 'add-seperators' );
  59. }
  60. }
  61. addNavigationSeparators( siteMenu );
  62. /**
  63. * Re-initialize the main navigation when it is updated in the customizer.
  64. *
  65. * Borrowed from twentysixteen.
  66. *
  67. * @link https://goo.gl/O6msL1
  68. */
  69. $( document ).on( 'customize-preview-menu-refreshed', function( e, params ) {
  70. if ( 'header' === params.wpNavMenuArgs.theme_location ) {
  71. initMainNavigation( params.newContainer );
  72. // Add separators to live customizer preview
  73. addNavigationSeparators( params.newContainer.parents('nav') );
  74. }
  75. });
  76. // Enable menuToggle.
  77. ( function() {
  78. // Return early if menuToggle is missing.
  79. if ( ! menuToggle.length ) {
  80. return;
  81. }
  82. // Add an initial values for the attribute.
  83. menuToggle.add( siteNavigation ).attr( 'aria-expanded', 'false' );
  84. menuToggle.on( 'click.textbook', function() {
  85. var _this = $( this );
  86. _this.add( siteMenu ).add( siteNavigation ).toggleClass( 'toggled-on' );
  87. // jscs:disable
  88. _this.add( siteMenu ).add( siteNavigation ).attr( 'aria-expanded', _this.add( siteNavigation ).attr( 'aria-expanded' ) === 'false' ? 'true' : 'false' );
  89. // jscs:enable
  90. _this.text( _this.text() === textBookMenuToggleText.menu ? textBookMenuToggleText.close : textBookMenuToggleText.menu );
  91. } );
  92. } )();
  93. // Fix sub-menus for touch devices and better focus for hidden submenu items for accessibility.
  94. ( function() {
  95. if ( ! siteNavigation.length || ! siteNavigation.children().length ) {
  96. return;
  97. }
  98. // Toggle `focus` class to allow submenu access on tablets.
  99. function toggleFocusClassTouchScreen() {
  100. if ( window.innerWidth >= 896 ) {
  101. $( document.body ).on( 'touchstart.textbook', function( e ) {
  102. if ( ! $( e.target ).closest( '.main-navigation li' ).length ) {
  103. $( '.main-navigation li' ).removeClass( 'focus' );
  104. }
  105. } );
  106. siteNavigation.find( '.menu-item-has-children > a, .page_item_has_children > a' ).on( 'touchstart.textbook', function( e ) {
  107. var el = $( this ).parent( 'li' );
  108. if ( ! el.hasClass( 'focus' ) ) {
  109. e.preventDefault();
  110. el.toggleClass( 'focus' );
  111. el.siblings( '.focus' ).removeClass( 'focus' );
  112. }
  113. } );
  114. siteNavigation.find( 'button' ).attr( 'tabindex', '-1' );
  115. } else {
  116. siteNavigation.find( '.menu-item-has-children > a, .page_item_has_children > a' ).unbind( 'touchstart.textbook' );
  117. siteNavigation.find( 'button' ).removeAttr( 'tabindex' );
  118. }
  119. }
  120. if ( 'ontouchstart' in window ) {
  121. $( window ).on( 'resize.textbook', toggleFocusClassTouchScreen );
  122. toggleFocusClassTouchScreen();
  123. }
  124. siteNavigation.find( 'a' ).on( 'focus.textbook blur.textbook', function() {
  125. $( this ).parents( '.menu-item, .page_item' ).toggleClass( 'focus' );
  126. } );
  127. } )();
  128. // Add the default ARIA attributes for the menu toggle and the navigations.
  129. function onResizeARIA() {
  130. if ( window.innerWidth < 896 ) {
  131. if ( menuToggle.hasClass( 'toggled-on' ) ) {
  132. menuToggle.attr( 'aria-expanded', 'true' );
  133. siteMenu.attr( 'aria-expanded', 'true' );
  134. siteNavigation.attr( 'aria-expanded', 'true' );
  135. } else {
  136. menuToggle.attr( 'aria-expanded', 'false' );
  137. siteMenu.attr( 'aria-expanded', 'false' );
  138. siteNavigation.attr( 'aria-expanded', 'false' );
  139. }
  140. } else {
  141. menuToggle.removeAttr( 'aria-expanded' );
  142. siteMenu.removeAttr( 'aria-expanded' );
  143. siteNavigation.removeAttr( 'aria-expanded' );
  144. }
  145. }
  146. $( document ).ready( function() {
  147. body = $( document.body );
  148. $( window )
  149. .on( 'load.textbook', onResizeARIA )
  150. .on( 'resize.textbook', onResizeARIA );
  151. } );
  152. } )( jQuery );