1/** 2 * MMT header keyboard/toggle support (owner report 2026-08-22). 3 * 4 * frontend/views/widgets/headerWidget/menu.php's "More destinations" / 5 * per-menu-item submenu triggers, the currency selector, and the header 6 * "Free Quote" CTA were all plain <div>s: opened only by CSS :hover (submenu/ 7 * currency) or not focusable at all (the CTA), so a keyboard-only visitor 8 * could never reach them. This file is registered ONLY on the MMT tenant 9 * (menu.php's own $isMyMediTravel gate), so it never runs for the other 10 * tenants sharing that same template. 11 * 12 * Deliberately additive, not a replacement for the existing CSS :hover 13 * rules: mouse users see byte-identical behavior to before. A click/ 14 * keyboard toggle just ALSO opens the same panel via a class, independent 15 * of :hover. 16 */ 17(function () { 18 'use strict'; 19 20 function isTypingTarget(el) { 21 var tag = el && el.tagName; 22 return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT'; 23 } 24 25 // ---- Enter/Space activates any role=button div (submenu triggers, the 26 // Free Quote CTA) exactly like a native <button> would. ---- 27 document.addEventListener('keydown', function (e) { 28 if (e.key !== 'Enter' && e.key !== ' ' && e.key !== 'Spacebar') { return; } 29 var t = e.target; 30 if (!t || !t.getAttribute || t.getAttribute('role') !== 'button') { return; } 31 if (isTypingTarget(t)) { return; } 32 e.preventDefault(); 33 try { t.click(); } catch (err) { /* never fatal */ } 34 }); 35 36 // ---- Currency selector: click/keyboard toggle, Escape + outside-click 37 // close. Multiple triggers can exist on the page (desktop header today); 38 // querySelectorAll + delegated listeners cover any of them. ---- 39 function closeAllCurrency(except) { 40 var openTriggers = document.querySelectorAll('.currency-wrap-right-box[aria-expanded="true"]'); 41 for (var i = 0; i < openTriggers.length; i++) { 42 if (openTriggers[i] === except) { continue; } 43 openTriggers[i].setAttribute('aria-expanded', 'false'); 44 openTriggers[i].classList.remove('mmt-open'); 45 } 46 } 47 48 function closeAllSubmenus(except) { 49 var openTriggers = document.querySelectorAll('.menu-side__sub-menu__title[aria-expanded="true"]'); 50 for (var i = 0; i < openTriggers.length; i++) { 51 if (openTriggers[i] === except) { continue; } 52 openTriggers[i].setAttribute('aria-expanded', 'false'); 53 openTriggers[i].classList.remove('mmt-open'); 54 } 55 } 56 57 document.addEventListener('click', function (e) { 58 var t = e.target; 59 if (!t || !t.closest) { return; } 60 61 var currencyTrigger = t.closest('.currency-wrap-right-box[role="button"]'); 62 if (currencyTrigger) { 63 // A currency <a> inside was the real click target - let its own 64 // handler (main-fixspeed.js / main-new-enquiry.js) navigate, 65 // just close the panel behind it. 66 if (t.closest('a[data-currency]')) { 67 closeAllCurrency(null); 68 return; 69 } 70 var nowOpen = currencyTrigger.getAttribute('aria-expanded') !== 'true'; 71 closeAllCurrency(null); 72 closeAllSubmenus(null); 73 currencyTrigger.setAttribute('aria-expanded', nowOpen ? 'true' : 'false'); 74 currencyTrigger.classList.toggle('mmt-open', nowOpen); 75 return; 76 } 77 78 var submenuTrigger = t.closest('.menu-side__sub-menu__title[role="button"]'); 79 if (submenuTrigger) { 80 var subNowOpen = submenuTrigger.getAttribute('aria-expanded') !== 'true'; 81 closeAllSubmenus(null); 82 closeAllCurrency(null); 83 submenuTrigger.setAttribute('aria-expanded', subNowOpen ? 'true' : 'false'); 84 submenuTrigger.classList.toggle('mmt-open', subNowOpen); 85 return; 86 } 87 88 // Outside click: close everything. 89 closeAllCurrency(null); 90 closeAllSubmenus(null); 91 }); 92 93 document.addEventListener('keydown', function (e) { 94 if (e.key !== 'Escape' && e.key !== 'Esc') { return; } 95 closeAllCurrency(null); 96 closeAllSubmenus(null); 97 }); 98})();
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.