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 8let mmToggli, mmToggla, mmBody, mmDrawer, mmBacker, mmClones, mmTargets; 9 10document.addEventListener("DOMContentLoaded", function() { 11 let collection, container, menu, links, i, len; 12 13 // collect the menus for mobile 14 collection = document.querySelectorAll( '.main-navigation' ); 15 16 // get the toggle menu item (li) 17 mmToggli = document.querySelector( '.main-navigation__toggle' ); 18 19 // Exit early if collection is empty or the toggle button is undefined 20 if ( 0 === collection.length || null === mmToggli || 'undefined' === typeof mmToggli ) { 21 console.log( 'Mobile navigation skipped' ); 22 return; 23 } 24 25 // the top-most element containing content (i.e. body) 26 mmBody = document.getElementsByTagName( 'body' )[0]; 27 28 mmDrawer = document.getElementById( 'menu-drawer' ); 29 mmBacker = document.getElementById( 'menu-backer' ); 30 mmClones = document.getElementById( 'menu-clones' ); 31 32 mmTargets = []; // array of expanded menus in the menu drawer 33 34 // reverse the collection so .main-navigation renders first in the .menu-drawer 35 collection = Array.prototype.slice.call( collection ); 36 collection.reverse(); 37 38 // Toggle click event. 39 mmToggli.onclick = function(e) { 40 if ( ! mmDrawer.classList.contains( 'toggled' ) ) { 41 openMenuDrawer(); 42 } else { 43 closeMenuDrawer(); 44 } 45 }; 46 47 collection.forEach( container => { 48 menu = container.querySelector( 'ul' ); 49 50 // put a clone of the menu in the menu clones container 51 let clone = menu.cloneNode( true ); 52 mmClones.appendChild( clone ); 53 54 if ( -1 === menu.className.indexOf( 'nav-menu' ) ) { 55 menu.className += ' nav-menu'; 56 } 57 } ); 58 59 mmClones.querySelectorAll( '*' ).forEach( item => { 60 if( item.id ) { 61 item.id = item.id + "-drawer"; 62 } 63 } ); 64 65 // Assign the "menu-item-has-children" class to the li elements with ul as child. 66 mmDrawer.querySelectorAll( 'li' ).forEach( item => { 67 for (const child of item.children) { 68 if(child.tagName === 'UL') { 69 item.classList.add('menu-item-has-children'); 70 71 // Add submenu header 72 const submenuHeader = document.createElement('li'); 73 submenuHeader.setAttribute('class', 'menu-header'); 74 submenuHeader.innerHTML = item.innerHTML; 75 child.prepend(submenuHeader); 76 } 77 } 78 }); 79 80 mmDrawer.querySelectorAll( '.menu-item-has-children' ).forEach( item => { 81 item.addEventListener( 'click', function( event ) { 82 // stop propagation of child elements 83 item.querySelectorAll( 'li:not(.menu-item-has-children)' ).forEach( child => { 84 child.addEventListener( 'click', function ( event ) { 85 event.stopPropagation(); 86 } ); 87 } ); 88 89 item.classList.add( 'expanded' ); 90 mmTargets.push( this ); 91 slideMenus( this ); 92 scrollMenuToTop(); 93 event.stopPropagation(); 94 event.preventDefault(); 95 } ); 96 } ); 97 98 mmBacker.addEventListener( 'click', function( event ) { 99 if( mmTargets.length > 0 ) { 100 let collapse = mmTargets.pop(); 101 collapse.classList.remove( 'expanded' ); 102 slideMenus( this ); 103 scrollMenuToTop(); 104 } 105 else { 106 closeMenuDrawer(); 107 } 108 109 event.preventDefault(); 110 } ); 111} ); 112 113function openMenuDrawer() { 114 backerContext(); 115 mmDrawer.querySelectorAll( 'a' ).forEach( item => { 116 item.tabIndex = 0; 117 } ); 118 mmBody.classList.add( 'menu-drawer-toggled'); 119 mmDrawer.classList.add( 'toggled' ); 120 mmToggli.classList.add( 'open' ); 121 mmToggli.focus(); 122} 123 124function closeMenuDrawer() { 125 // clear the expanded targets array 126 mmTargets = []; 127 128 // remove the expanded class from all menu items 129 mmDrawer.querySelectorAll( '.expanded' ).forEach( item => { 130 item.classList.remove( 'expanded' ); 131 } ); 132 133 // remove collapsed items from the tabindex 134 mmDrawer.querySelectorAll( 'a' ).forEach( item => { 135 item.tabIndex = -1; 136 } ); 137 138 // remove clone transformations 139 mmClones.style.transform = "none"; 140 141 mmBody.classList.remove( 'menu-drawer-toggled'); 142 mmDrawer.classList.remove( 'toggled' ); 143 mmToggli.classList.remove( 'open' ); 144 mmToggli.focus(); 145} 146 147function slideMenus( target ) { 148 backerContext(); 149 target.classList.add( 'expanded' ); 150 mmClones.style.transform = "translateX( -" + (100 * mmTargets.length) + "% )"; 151} 152 153function backerContext() { 154 if( mmTargets.length > 0 ) { 155 mmBacker.innerHTML = 'Previous Menu'; 156 } 157 else { 158 mmBacker.innerHTML = 'Close Menu'; 159 } 160} 161 162/** 163 * Scrolls the menu drawer to top. 164 */ 165function scrollMenuToTop() { 166 mmDrawer.scrollTo( 0, 0 ); 167} 168 169/**
170 * Sets or removes .focus class on an element. 171 */ 172function toggleFocus( menuItem ) { 173 mmDrawer.querySelectorAll( '.focus' ).forEach( focusItem => function() { 174 focusItem.classList.remove( 'focus' ); 175 } ); 176 menuItem.closest( 'li' ).classList.add( 'focus' ); 177}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.