navigation.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. /* global rebalanceScreenReaderText */
  2. ( function( $ ) {
  3. var body, masthead, menuToggle, siteMenu, siteNavigation;
  4. function initMainNavigation( container ) {
  5. /**
  6. * Use 3 column display in main menu
  7. * - break top-level lists into three parts
  8. * - add parent class to top-level menu items
  9. */
  10. container.find('.menu, .nav-menu').columnlist( { size : 3 } );
  11. container.find( '.sub-menu,.children' ).parents( 'li' ).addClass( 'menu-item-has-children' );
  12. // Add dropdown toggle that displays child menu items.
  13. var dropdownToggle = $( '<button />', {
  14. 'class': 'dropdown-toggle',
  15. 'aria-expanded': false
  16. } ).append( $( '<span />', {
  17. 'class': 'screen-reader-text',
  18. text: rebalanceScreenReaderText.expand
  19. } ) );
  20. container.find( '.menu-item-has-children > a' ).after( dropdownToggle );
  21. // Toggle buttons and submenu items with active children menu items.
  22. container.find( '.current-menu-ancestor > button' ).addClass( 'toggled-on' );
  23. container.find( '.current-menu-ancestor > .sub-menu' ).addClass( 'toggled-on' );
  24. // Add menu items with submenus to aria-haspopup="true".
  25. container.find( '.menu-item-has-children' ).attr( 'aria-haspopup', 'true' );
  26. container.find( '.dropdown-toggle' ).click( function( e ) {
  27. var _this = $( this ),
  28. screenReaderSpan = _this.find( '.screen-reader-text' );
  29. e.preventDefault();
  30. _this.toggleClass( 'toggled-on' );
  31. _this.next( '.children, .sub-menu' ).toggleClass( 'toggled-on' );
  32. // jscs:disable
  33. _this.attr( 'aria-expanded', _this.attr( 'aria-expanded' ) === 'false' ? 'true' : 'false' );
  34. // jscs:enable
  35. screenReaderSpan.text( screenReaderSpan.text() === rebalanceScreenReaderText.expand ? rebalanceScreenReaderText.collapse : rebalanceScreenReaderText.expand );
  36. } );
  37. }
  38. initMainNavigation( $( '.main-navigation' ) );
  39. /**
  40. * Re-initialize the main navigation when it is updated in the customizer
  41. * - Borrowed from twentysixteen: https://goo.gl/O6msL1
  42. */
  43. $( document ).on( 'customize-preview-menu-refreshed', function( e, params ) {
  44. if ( 'header' === params.wpNavMenuArgs.theme_location ) {
  45. initMainNavigation( params.newContainer );
  46. }
  47. });
  48. masthead = $( '#masthead' );
  49. menuToggle = masthead.find( '.menu-toggle' );
  50. siteMenu = masthead.find( '.main-navigation' );
  51. siteNavigation = masthead.find( '.main-navigation > div' );
  52. // Enable menuToggle.
  53. ( function() {
  54. // Return early if menuToggle is missing.
  55. if ( ! menuToggle.length ) {
  56. return;
  57. }
  58. // Add an initial values for the attribute.
  59. menuToggle.add( siteNavigation ).attr( 'aria-expanded', 'false' );
  60. menuToggle.on( 'click.rebalance', function() {
  61. $( this ).add( siteMenu ).add( siteNavigation ).toggleClass( 'toggled-on' );
  62. // jscs:disable
  63. $( this ).add( siteMenu ).add( siteNavigation ).attr( 'aria-expanded', $( this ).add( siteNavigation ).attr( 'aria-expanded' ) === 'false' ? 'true' : 'false' );
  64. // jscs:enable
  65. } );
  66. } )();
  67. // Fix sub-menus for touch devices and better focus for hidden submenu items for accessibility.
  68. ( function() {
  69. if ( ! siteNavigation.length || ! siteNavigation.children().length ) {
  70. return;
  71. }
  72. // Toggle `focus` class to allow submenu access on tablets.
  73. function toggleFocusClassTouchScreen() {
  74. if ( window.innerWidth >= 896 ) {
  75. $( document.body ).on( 'touchstart.rebalance', function( e ) {
  76. if ( ! $( e.target ).closest( '.main-navigation li' ).length ) {
  77. $( '.main-navigation li' ).removeClass( 'focus' );
  78. }
  79. } );
  80. siteNavigation.find( '.menu-item-has-children > a' ).on( 'touchstart.rebalance', function( e ) {
  81. var el = $( this ).parent( 'li' );
  82. if ( ! el.hasClass( 'focus' ) ) {
  83. e.preventDefault();
  84. el.toggleClass( 'focus' );
  85. el.siblings( '.focus' ).removeClass( 'focus' );
  86. }
  87. } );
  88. } else {
  89. siteNavigation.find( '.menu-item-has-children > a' ).unbind( 'touchstart.rebalance' );
  90. }
  91. }
  92. if ( 'ontouchstart' in window ) {
  93. $( window ).on( 'resize.rebalance', toggleFocusClassTouchScreen );
  94. toggleFocusClassTouchScreen();
  95. }
  96. siteNavigation.find( 'a' ).on( 'focus.rebalance blur.rebalance', function() {
  97. $( this ).parents( '.menu-item' ).toggleClass( 'focus' );
  98. } );
  99. } )();
  100. // Add the default ARIA attributes for the menu toggle and the navigations.
  101. function onResizeARIA() {
  102. if ( window.innerWidth < 896 ) {
  103. if ( menuToggle.hasClass( 'toggled-on' ) ) {
  104. menuToggle.attr( 'aria-expanded', 'true' );
  105. siteMenu.attr( 'aria-expanded', 'true' );
  106. siteNavigation.attr( 'aria-expanded', 'true' );
  107. } else {
  108. menuToggle.attr( 'aria-expanded', 'false' );
  109. siteMenu.attr( 'aria-expanded', 'false' );
  110. siteNavigation.attr( 'aria-expanded', 'false' );
  111. }
  112. } else {
  113. menuToggle.removeAttr( 'aria-expanded' );
  114. siteMenu.removeAttr( 'aria-expanded' );
  115. siteNavigation.removeAttr( 'aria-expanded' );
  116. }
  117. }
  118. $( document ).ready( function() {
  119. body = $( document.body );
  120. $( window )
  121. .on( 'load.rebalance', onResizeARIA )
  122. .on( 'resize.rebalance', onResizeARIA );
  123. } );
  124. } )( jQuery );