PageSourceSearch

https://www.mymeditravel.com/js/header-a11y-fix.js?v=1790948639

js mymeditravel.com collected 2026-10-02 16:15:46 UTC 4,227 bytes, 98 lines download raw bytes

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.