1let container; 2let trigger; 3let arrow; 4let selectedText; 5let optionsList; 6let options; 7let hiddenField; 8let isExpanded = false; 9 10window.addEventListener('DOMContentLoaded', function (e) { 11 container = document.querySelector('.custom-select'); 12 trigger = document.querySelector('.custom-select .trigger'); 13 arrow = document.querySelector('.custom-select .icon'); 14 selectedText = document.getElementById('selected-text'); 15 optionsList = document.querySelector('.custom-select .options'); 16 options = document.querySelectorAll('.custom-select .options li'); 17 hiddenField = document.getElementById('hidden-field'); 18 19 // Handle keyboard and mouse interactions on the trigger bar 20 if (trigger) { 21 trigger.addEventListener('keydown', handleTriggerKeypresses); 22 trigger.addEventListener('click', handleTriggerClick); 23 24 // Select options when they are clicked 25 options.forEach(function (option) { 26 option.addEventListener('click', function (e) { 27 unselectAllOptions(); 28 selectOption(e.target); 29 collapseOptions(); 30 trigger.focus(); 31 }); 32 }); 33 34 // Collapse the dropdown when the user clicks off 35 document.addEventListener('click', handleBodyClick); 36 } 37}); 38 39 40/** 41 Event handlers 42 */ 43function handleTriggerKeypresses (e) { 44 switch (e.key) { 45 case 'ArrowUp': 46 e.preventDefault(); 47 48 if (isExpanded) { 49 selectPreviousOption(); 50 } else { 51 expandOptions(); 52 } 53 54 break; 55 56 case 'ArrowDown': 57 e.preventDefault(); 58 59 if (isExpanded) { 60 selectNextOption(); 61 } else { 62 expandOptions(); 63 selectFirstOption(); 64 } 65 66 break; 67 68 case 'Home': 69 if (isExpanded) { 70 selectFirstOption(); 71 } 72 73 break; 74 75 case 'End': 76 if (isExpanded) { 77 selectLastOption(); 78 } 79 80 break; 81 82 case 'Tab': 83 collapseOptions(); 84 break; 85 86 case 'Escape': 87 case 'Enter': 88 89 if (isExpanded) { 90 e.preventDefault(); 91 collapseOptions(e); 92 } 93 94 break; 95 } 96} 97 98function handleBodyClick (e) { 99 if (!container.contains(e.target) && isExpanded) { 100 collapseOptions(); 101 } 102} 103 104function handleTriggerClick () { 105 if (isExpanded) { 106 collapseOptions(); 107 trigger.focus(); 108 } else { 109 expandOptions(); 110 } 111} 112 113/** 114 * Toggle arrow position 115 */ 116function toggleArrowPosition (shouldToggle) { 117 if (shouldToggle) { 118 arrow.classList.add('expanded'); 119 } else { 120 arrow.classList.remove('expanded'); 121 } 122} 123 124 125/** 126 Show/hide option list 127 */ 128function collapseOptions (e = null) { 129 trigger.setAttribute('aria-expanded', false); 130 optionsList.classList.remove('is-visible'); 131 isExpanded = false; 132 toggleArrowPosition(false); 133 134 if (e) { 135 let currentOption = document.getElementById(trigger.getAttribute('aria-activedescendant')); 136 137 if (currentOption !== undefined && e.key == 'Enter') { 138 let currentOptionIndex = Array.prototype.slice.call(options).indexOf(currentOption); 139 140 options[currentOptionIndex].click(); 141 } 142 } 143} 144 145function expandOptions () { 146 trigger.setAttribute('aria-expanded', true); 147 optionsList.classList.add('is-visible'); 148 isExpanded = true; 149 toggleArrowPosition(true); 150} 151 152/** 153 Option selection 154 */ 155function selectFirstOption () { 156 let firstOption = options[0]; 157 selectOption(firstOption); 158} 159 160function selectLastOption () { 161 let lastOption = options[options.length - 1]; 162 selectOption(lastOption); 163} 164 165function selectNextOption () { 166 let currentOption = document.getElementById(trigger.getAttribute('aria-activedescendant')); 167 168 if (currentOption !== undefined) { 169 let currentOptionIndex = Array.prototype.slice.call(options).indexOf(currentOption); 170 171 if (currentOptionIndex + 1 < options.length) { 172 let nextOption = options[currentOptionIndex + 1]; 173 selectOption(nextOption); 174 } 175 } 176} 177 178function selectPreviousOption () { 179 let currentOption = document.getElementById(trigger.getAttribute('aria-activedescendant')); 180 181 if (currentOption !== undefined) { 182 let currentOptionIndex = Array.prototype.slice.call(options).indexOf(currentOption); 183 184 if (currentOptionIndex - 1 >= 0) { 185 let previousOption = options[currentOptionIndex - 1]; 186 selectOption(previousOption); 187 } 188 } 189} 190 191function selectOption (option) { 192 unselectAllOptions(); 193 option.setAttribute('aria-selected', true); 194 trigger.setAttribute('aria-activedescendant', option.getAttribute('id')); 195 selectedText.innerHTML = option.innerHTML; 196 197 // Update the hidden field 198 hiddenField.value = option.innerHTML; 199} 200 201function unselectAllOptions () { 202 options.forEach(function (option) { 203 option.removeAttribute('aria-selected'); 204 }); 205}
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.