navigation.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  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. const siteNavigation = document.getElementById( 'site-navigation' );
  9. // Return early if the navigation doesn't exist.
  10. if ( ! siteNavigation ) {
  11. return;
  12. }
  13. const button = siteNavigation.getElementsByTagName( 'button' )[ 0 ];
  14. // Return early if the button doesn't exist.
  15. if ( 'undefined' === typeof button ) {
  16. return;
  17. }
  18. const menu = siteNavigation.getElementsByTagName( 'ul' )[ 0 ];
  19. // Hide menu toggle button if menu is empty and return early.
  20. if ( 'undefined' === typeof menu ) {
  21. button.style.display = 'none';
  22. return;
  23. }
  24. if ( ! menu.classList.contains( 'nav-menu' ) ) {
  25. menu.classList.add( 'nav-menu' );
  26. }
  27. // Toggle the .toggled class and the aria-expanded value each time the button is clicked.
  28. button.addEventListener( 'click', function() {
  29. siteNavigation.classList.toggle( 'toggled' );
  30. if ( button.getAttribute( 'aria-expanded' ) === 'true' ) {
  31. button.setAttribute( 'aria-expanded', 'false' );
  32. } else {
  33. button.setAttribute( 'aria-expanded', 'true' );
  34. }
  35. } );
  36. // Remove the .toggled class and set aria-expanded to false when the user clicks outside the navigation.
  37. document.addEventListener( 'click', function( event ) {
  38. const isClickInside = siteNavigation.contains( event.target );
  39. if ( ! isClickInside ) {
  40. siteNavigation.classList.remove( 'toggled' );
  41. button.setAttribute( 'aria-expanded', 'false' );
  42. }
  43. } );
  44. // Get all the link elements within the menu.
  45. const links = menu.getElementsByTagName( 'a' );
  46. // Get all the link elements with children within the menu.
  47. const linksWithChildren = menu.querySelectorAll( '.menu-item-has-children > a, .page_item_has_children > a' );
  48. // Toggle focus each time a menu link is focused or blurred.
  49. for ( const link of links ) {
  50. link.addEventListener( 'focus', toggleFocus, true );
  51. link.addEventListener( 'blur', toggleFocus, true );
  52. }
  53. // Toggle focus each time a menu link with children receive a touch event.
  54. for ( const link of linksWithChildren ) {
  55. link.addEventListener( 'touchstart', toggleFocus, false );
  56. }
  57. /**
  58. * Sets or removes .focus class on an element.
  59. */
  60. function toggleFocus() {
  61. if ( event.type === 'focus' || event.type === 'blur' ) {
  62. let self = this;
  63. // Move up through the ancestors of the current link until we hit .nav-menu.
  64. while ( ! self.classList.contains( 'nav-menu' ) ) {
  65. // On li elements toggle the class .focus.
  66. if ( 'li' === self.tagName.toLowerCase() ) {
  67. self.classList.toggle( 'focus' );
  68. }
  69. self = self.parentNode;
  70. }
  71. }
  72. if ( event.type === 'touchstart' ) {
  73. const menuItem = this.parentNode;
  74. event.preventDefault();
  75. for ( const link of menuItem.parentNode.children ) {
  76. if ( menuItem !== link ) {
  77. link.classList.remove( 'focus' );
  78. }
  79. }
  80. menuItem.classList.toggle( 'focus' );
  81. }
  82. }
  83. }() );