1const accessibleMenu = function (domNode) { 2 this.rootNode = domNode; 3 this.triggerNodes = []; 4 this.controlledNodes = []; 5 this.openIndex = null; 6 this.useArrowKeys = true; 7 }; 8 9 accessibleMenu.prototype.init = function () { 10 const buttons = this.rootNode.querySelectorAll('.nav-dropdown'); 11 for (var i = 0; i < buttons.length; i++) { 12 const button = buttons[i]; 13 const menu = button.parentNode.querySelector('.nav-submenu'); 14 if (menu) { 15 // Save ref to button and controlled menu. 16 this.triggerNodes.push(button); 17 this.controlledNodes.push(menu); 18 19 // Collapse menus. 20 button.setAttribute('aria-expanded', 'false'); 21 this.toggleMenu(menu, false); 22 23 // Attach event listeners. 24 menu.addEventListener('keydown', this.handleMenuKeyDown.bind(this)); 25 button.addEventListener('click', this.handleButtonClick.bind(this)); 26 button.addEventListener('keydown', this.handleButtonKeyDown.bind(this)); 27 } 28 } 29 30 this.rootNode.addEventListener('focusout', this.handleBlur.bind(this)); 31 32 // Show the menu help instructions when the first button is focused. 33 // const menuHelp = document.querySelector('.menu-help-instructions'); 34 // if (buttons[0].getAttribute('aria-expanded') === 'false') { 35 // buttons[0].addEventListener('focusin', () => { 36 37 // window.setTimeout(() => { 38 // menuHelp.classList.remove('sr-only'); 39 // }, 200); 40 // }); 41 42 // buttons[0].addEventListener('focusout', () => { 43 // menuHelp.classList.add('sr-only'); 44 // }); 45 // } 46 }; 47 48 accessibleMenu.prototype.toggleMenu = function (domNode, show) { 49 if (domNode) { 50 domNode.style.display = show ? 'flex' : 'none'; 51 domNode.style.visibility = show ? 'visible' : 'hidden'; 52 domNode.classList.toggle('menu-is-open', show); 53 if ( show && domNode.getAttribute('id') == 'id_menu_search' ) { 54 document.getElementById('menu-search').focus() 55 document.getElementById('menu-search').select() 56 } 57 } 58 }; 59 60 accessibleMenu.prototype.toggleExpand = function (index, expanded) { 61 // Close open menu, if applicable. 62 if (this.openIndex !== index) { 63 this.toggleExpand(this.openIndex, false); 64 } 65 66 // console.log( this.openIndex ) 67 // const menus = document.querySelectorAll('.menu-is-open'); 68 // for (var i = 0; i < menus.length; i++) { 69 // menus[i].classList.remove('menu-is-open'); 70 71 // document.querySelector('.nav-toggle').classList.remove('active'); 72 // document.querySelector('.nav-toggle').setAttribute("aria-expanded", 'false'); 73 // } 74 75 // Handle menu at called index. 76 if (this.triggerNodes[index]) { 77 this.openIndex = expanded ? index : null; 78 this.triggerNodes[index].setAttribute('aria-expanded', expanded); 79 this.toggleMenu(this.controlledNodes[index], expanded); 80 } 81 }; 82 83 accessibleMenu.prototype.controlFocusByKey = function (keyboardEvent, nodeList, currentIndex) { 84 switch (keyboardEvent.key) { 85 case 'ArrowUp': 86 case 'ArrowLeft': 87 keyboardEvent.preventDefault(); 88 if (currentIndex > -1) { 89 const prevIndex = Math.max(0, currentIndex - 1); 90 nodeList[prevIndex].focus(); 91 } 92 break; 93 case 'ArrowDown': 94 case 'ArrowRight': 95 keyboardEvent.preventDefault(); 96 if (currentIndex > -1) { 97 const nextIndex = Math.min(nodeList.length - 1, currentIndex + 1); 98 nodeList[nextIndex].focus(); 99 } 100 break; 101 case 'Home': 102 keyboardEvent.preventDefault(); 103 nodeList[0].focus(); 104 break; 105 case 'End': 106 keyboardEvent.preventDefault(); 107 nodeList[nodeList.length - 1].focus(); 108 break; 109 } 110 }; 111 112 /* Event Handlers */ 113 accessibleMenu.prototype.handleBlur = function (event) { 114 const menuContainsFocus = this.rootNode.contains(event.relatedTarget); 115 if (!menuContainsFocus && this.openIndex !== null) { 116 this.toggleExpand(this.openIndex, false); 117 } 118 }; 119 120 accessibleMenu.prototype.handleButtonKeyDown = function (event) { 121 const targetButtonIndex = this.triggerNodes.indexOf(document.activeElement); 122 123 // Close the menu on the escape key. 124 if (event.key === 'Escape') { 125 this.toggleExpand(this.openIndex, false); 126 } 127 128 // Move focus into the open menu if the current menu is open. 129 else if (this.useArrowKeys && this.openIndex === targetButtonIndex && event.key === 'ArrowDown') { 130 event.preventDefault(); 131 this.controlledNodes[this.openIndex].querySelector('a').focus(); 132 } 133 134 // Handle arrow key navigation between top-level buttons, if set. 135 else if (this.useArrowKeys) { 136 this.controlFocusByKey(event, this.triggerNodes, targetButtonIndex); 137 } 138 }; 139 140 accessibleMenu.prototype.handleButtonClick = function (event) { 141 const button = event.target; 142 const buttonIndex = this.triggerNodes.indexOf(button); 143 const buttonExpanded = button.getAttribute('aria-expanded') === 'true'; 144 this.toggleExpand(buttonIndex, !buttonExpanded); 145 }; 146 147 accessibleMenu.prototype.handleMenuKeyDown = function (event) { 148 if (this.openIndex === null) { 149 return; 150 } 151 152 const menuLinks = Array.prototype.slice.call(this.controlledNodes[this.openIndex].querySelectorAll('a')); 153 const currentIndex = menuLinks.indexOf(document.activeElement); 154 155 // Close the menu on the escape key. 156 if (event.key === 'Escape') { 157 this.triggerNodes[this.openIndex].focus(); 158 this.toggleExpand(this.openIndex, false); 159 } 160 161 // Handle arrow key navigation within menu links, if set. 162 else if (this.useArrowKeys) { 163 this.controlFocusByKey(event, menuLinks, currentIndex); 164 } 165 }; 166 167 // Switch on/off arrow key navigation.
168 accessibleMenu.prototype.updateKeyControls = function (useArrowKeys) { 169 this.useArrowKeys = useArrowKeys; 170 }; 171 172 // Initialize Menus. 173 window.addEventListener('load', function (event) { 174 const menus = document.querySelectorAll('[role="menubar"]'); 175 const accessibleMenus = []; 176 177 for (var i = 0; i < menus.length; i++) { 178 accessibleMenus[i] = new accessibleMenu(menus[i]); 179 accessibleMenus[i].init(); 180 } 181 182 // Toggle Hamburger Button & Dropdown. 183 const hamburgerButtons = document.querySelectorAll('.nav-toggle'); 184 185 for (let o = 0; o < hamburgerButtons.length; o++) { 186 hamburgerButtons[o].addEventListener ('click', function ( menu ) { 187 188 hamburgerButtons[o].classList.toggle('active'); 189 190 for (var i = 0; i < accessibleMenus.length; i++) { 191 console.log( hamburgerButtons[o].getAttribute('aria-controls') ) 192 console.log( accessibleMenus[i].rootNode.getAttribute('id') ) 193 console.log( accessibleMenus[i].openIndex ) 194 if ( hamburgerButtons[o].getAttribute('aria-controls') == accessibleMenus[i].rootNode.getAttribute('id') ) { 195 accessibleMenus[i].toggleExpand( accessibleMenus[i].openIndex, false ); 196 197 if ( hamburgerButtons[o].getAttribute('aria-expanded') === 'false') { 198 // Set aria attribute. 199 hamburgerButtons[o].setAttribute("aria-expanded", 'true'); 200 //Apply toggle class to the first menu from arry. 201 accessibleMenus[i].rootNode.classList.add('menu-is-open'); 202 203 204 } else { 205 hamburgerButtons[o].setAttribute("aria-expanded", 'false'); 206 accessibleMenus[i].rootNode.classList.remove('menu-is-open'); 207 } 208 } 209 } 210 211 }); 212 } 213 214 }, false);
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.