navigation.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. /**
  2. * File navigation.js.
  3. *
  4. * Handles toggling the navigation menu for small screens and enables TAB key
  5. * navigation support for dropdown menus.
  6. */
  7. ( function( $ ) {
  8. var body, menuToggle, siteMenu, siteNavigation;
  9. function initMainNavigation( container ) {
  10. // Add dropdown toggle that displays child menu items.
  11. var dropdownToggle = $( '<button />', {
  12. 'class': 'dropdown-toggle',
  13. 'aria-expanded': false
  14. } ).append( $( '<span />', {
  15. 'class': 'screen-reader-text',
  16. text: screenReaderText.expand
  17. } ) );
  18. container.find( '.menu-item-has-children > a' ).after( dropdownToggle );
  19. // Toggle buttons and submenu items with active children menu items.
  20. container.find( '.current-menu-ancestor > button' ).addClass( 'toggled-on' );
  21. container.find( '.current-menu-ancestor > .sub-menu' ).addClass( 'toggled-on' );
  22. // Add menu items with submenus to aria-haspopup="true".
  23. container.find( '.menu-item-has-children' ).attr( 'aria-haspopup', 'true' );
  24. container.find( '.dropdown-toggle' ).click( function( e ) {
  25. var _this = $( this ),
  26. screenReaderSpan = _this.find( '.screen-reader-text' );
  27. e.preventDefault();
  28. _this.toggleClass( 'toggled-on' );
  29. _this.next( '.children, .sub-menu' ).toggleClass( 'toggled-on' );
  30. // jscs:disable
  31. _this.attr( 'aria-expanded', _this.attr( 'aria-expanded' ) === 'false' ? 'true' : 'false' );
  32. // jscs:enable
  33. screenReaderSpan.text( screenReaderSpan.text() === screenReaderText.expand ? screenReaderText.collapse : screenReaderText.expand );
  34. } );
  35. }
  36. initMainNavigation( $( '#site-navigation' ) );
  37. siteMenu = $( '#menu-primary' );
  38. siteNavigation = $( '#site-navigation' );
  39. menuToggle = siteNavigation.find( '.menu-toggle' );
  40. // Enable menuToggle.
  41. ( function() {
  42. // Return early if menuToggle is missing.
  43. if ( ! menuToggle.length ) {
  44. return;
  45. }
  46. // Add an initial values for the attribute.
  47. menuToggle.add( siteNavigation ).attr( 'aria-expanded', 'false' );
  48. menuToggle.on( 'click.apostrophe', function() {
  49. $( this ).add( siteMenu ).add( siteNavigation ).toggleClass( 'toggled-on' );
  50. // jscs:disable
  51. $( this ).add( siteMenu ).add( siteNavigation ).attr( 'aria-expanded', $( this ).add( siteNavigation ).attr( 'aria-expanded' ) === 'false' ? 'true' : 'false' );
  52. // jscs:enable
  53. } );
  54. } )();
  55. // Fix sub-menus for touch devices and better focus for hidden submenu items for accessibility.
  56. ( function() {
  57. if ( ! siteNavigation.length || ! siteNavigation.children().length ) {
  58. return;
  59. }
  60. // Toggle `focus` class to allow submenu access on tablets.
  61. function toggleFocusClassTouchScreen() {
  62. if ( window.innerWidth >= 840 ) {
  63. $( document.body ).on( 'touchstart.apostrophe', function( e ) {
  64. if ( ! $( e.target ).closest( '.main-navigation li' ).length ) {
  65. $( '.main-navigation li' ).removeClass( 'focus' );
  66. }
  67. } );
  68. siteNavigation.find( '.menu-item-has-children > a' ).on( 'touchstart.apostrophe', function( e ) {
  69. var el = $( this ).parent( 'li' );
  70. if ( ! el.hasClass( 'focus' ) ) {
  71. e.preventDefault();
  72. el.toggleClass( 'focus' );
  73. el.siblings( '.focus' ).removeClass( 'focus' );
  74. }
  75. } );
  76. } else {
  77. siteNavigation.find( '.menu-item-has-children > a' ).unbind( 'touchstart.apostrophe' );
  78. }
  79. }
  80. if ( 'ontouchstart' in window ) {
  81. $( window ).on( 'resize.apostrophe', toggleFocusClassTouchScreen );
  82. toggleFocusClassTouchScreen();
  83. }
  84. siteNavigation.find( 'a' ).on( 'focus.apostrophe blur.apostrophe', function() {
  85. $( this ).parents( '.menu-item' ).toggleClass( 'focus' );
  86. } );
  87. } )();
  88. // Add the default ARIA attributes for the menu toggle and the navigations.
  89. function onResizeARIA() {
  90. if ( window.innerWidth < 840 ) {
  91. if ( menuToggle.hasClass( 'toggled-on' ) ) {
  92. menuToggle.attr( 'aria-expanded', 'true' );
  93. siteMenu.attr( 'aria-expanded', 'true' );
  94. siteNavigation.attr( 'aria-expanded', 'true' );
  95. } else {
  96. menuToggle.attr( 'aria-expanded', 'false' );
  97. siteMenu.attr( 'aria-expanded', 'false' );
  98. siteNavigation.attr( 'aria-expanded', 'false' );
  99. }
  100. } else {
  101. menuToggle.removeAttr( 'aria-expanded' );
  102. siteMenu.removeAttr( 'aria-expanded' );
  103. siteNavigation.removeAttr( 'aria-expanded' );
  104. }
  105. }
  106. $( document ).ready( function() {
  107. body = $( document.body );
  108. $( window )
  109. .on( 'load.apostrophe', onResizeARIA )
  110. .on( 'resize.apostrophe', onResizeARIA );
  111. } );
  112. } )( jQuery );