PageSourceSearch

https://omahaplayhouse.com/wp-content/themes/gmlaunch/js/header-dropdown.js?ver=1783443190

js omahaplayhouse.com collected 2026-10-02 10:29:36 UTC 12,831 bytes, 310 lines download raw bytes

1/**
2 * Header Dropdown Menu Functionality
3 * Handles both desktop hover-based dropdowns and mobile click-based dropdowns
4 */
5
6document.addEventListener('DOMContentLoaded', function() {
7    
8    // Desktop dropdown functionality
9    const dropdownItems = document.querySelectorAll('.header-nav-desktop_inner li.has-dropdown');
10
11    // Edge-aware positioning: a dropdown defaults to left-aligned under its item.
12    // If it would cross either viewport edge, nudge it horizontally by ONLY the
13    // overflow amount so it stays anchored under its parent (a full right-align
14    // flip threw short-label dropdowns like "About" far off to the left). We shift
15    // `left` (not `transform`) so the translateY open/close animation is preserved.
16    const EDGE = 16; // min viewport gutter, px
17    function positionDropdown(item) {
18        const dropdown = item.querySelector('.header-nav-dropdown');
19        if (!dropdown) { return; }
20        item.classList.remove('dd-align-right');   // legacy full-flip, no longer used
21        dropdown.style.left = '';                  // drop prior correction → measure natural (left:0) box
22        const vw = document.documentElement.clientWidth;
23        const rect = dropdown.getBoundingClientRect();
24        let shift = 0;
25        if (rect.right > vw - EDGE) { shift = (vw - EDGE) - rect.right; }  // overflow right → pull left
26        else if (rect.left < EDGE) { shift = EDGE - rect.left; }          // overflow left → push right
27        if (shift) { dropdown.style.left = Math.round(shift) + 'px'; }
28    }
29
30    // Re-clamp the open/hovered dropdown if the window resizes under it.
31    window.addEventListener('resize', function () {
32        if (window.innerWidth <= 1100) { return; }
33        const active = document.querySelector('.header-nav-desktop_inner li.has-dropdown.dropdown-open, .header-nav-desktop_inner li.has-dropdown:hover');
34        if (active) { positionDropdown(active); }
35    });
36
37    dropdownItems.forEach(function(item) {
38        const link = item.querySelector('a');
39        const dropdown = item.querySelector('.header-nav-dropdown');
40        
41        if (!link || !dropdown) {
42            return;
43        }
44
45        // Reposition on hover (desktop opens dropdowns via CSS :hover)
46        item.addEventListener('mouseenter', function() {
47            if (window.innerWidth > 1100) { positionDropdown(item); }
48        });
49
50        // Add click functionality for touch devices
51        link.addEventListener('click', function(e) {
52            // Only prevent default if this is a parent item with dropdown
53            if (window.innerWidth <= 1100) {
54                return; // Let mobile menu handle this
55            }
56
57            // Check if dropdown is currently open
58            const isOpen = item.classList.contains('dropdown-open');
59
60            // Close all other dropdowns
61            dropdownItems.forEach(function(otherItem) {
62                if (otherItem !== item) {
63                    otherItem.classList.remove('dropdown-open');
64                }
65            });
66
67            // Toggle current dropdown
68            if (isOpen) {
69                item.classList.remove('dropdown-open');
70            } else {
71                positionDropdown(item);
72                item.classList.add('dropdown-open');
73                e.preventDefault(); // Prevent navigation on click for parent items
74            }
75        });
76
77        // Add keyboard accessibility
78        link.addEventListener('keydown', function(e) {
79            if (e.key === 'Enter' || e.key === ' ') {
80                e.preventDefault();
81                link.click();
82            } else if (e.key === 'Escape') {
83                item.classList.remove('dropdown-open');
84            }
85        });
86
87        // Add focus/blur events for keyboard navigation
88        item.addEventListener('focusout', function(e) {
89            // Close dropdown when focus leaves the entire menu item
90            setTimeout(function() {
91                if (!item.contains(document.activeElement)) {
92                    item.classList.remove('dropdown-open');
93                }
94            }, 100);
95        });
96    });
97
98    // Mobile dropdown functionality
99    const mobileDropdownToggles = document.querySelectorAll('.mobile-dropdown-toggle');
100    const mobileParentLinks = document.querySelectorAll('.header-nav-mobile li.has-mobile-dropdown .mobile-main-link');
101    
102    function toggleMobileDropdown(parentLi) {
103        const toggle = parentLi.querySelector('.mobile-dropdown-toggle');
104        const toggleIcon = toggle.querySelector('.mobile-toggle-icon');
105        const submenu = parentLi.querySelector('.header-nav-mobile-submenu');
106        
107        if (!submenu || !toggleIcon) {
108            return;
109        }
110
111        // Close all other mobile dropdowns
112        document.querySelectorAll('.header-nav-mobile li.has-mobile-dropdown').forEach(function(otherLi) {
113            if (otherLi !== parentLi) {
114                const otherSubmenu = otherLi.querySelector('.header-nav-mobile-submenu');
115                const otherToggle = otherLi.querySelector('.mobile-dropdown-toggle');
116                const otherIcon = otherToggle ? otherToggle.querySelector('.mobile-toggle-icon') : null;
117                if (otherSubmenu && otherToggle && otherIcon) {
118                    otherSubmenu.classList.remove('open');
119                    otherToggle.classList.remove('active');
120                    otherToggle.setAttribute('aria-expanded', 'false');
121                    otherIcon.textContent = '+';
122                }
123            }
124        });
125
126        // Toggle current dropdown
127        const isOpen = submenu.classList.contains('open');
128        
129        if (isOpen) {
130            // Close dropdown
131            submenu.classList.remove('open');
132            toggle.classList.remove('active');
133            toggle.setAttribute('aria-expanded', 'false');
134            toggleIcon.textContent = '+';
135        } else {
136            // Open dropdown
137            submenu.classList.add('open');
138            toggle.classList.add('active');
139            toggle.setAttribute('aria-expanded', 'true');
140            toggleIcon.textContent = '−';
141        }
142    }
143    
144    // Make parent links clickable to toggle dropdown
145    mobileParentLinks.forEach(function(link) {
146        link.addEventListener('click', function(e) {
147            e.preventDefault();
148            const parentLi = link.closest('li.has-mobile-dropdown');
149            toggleMobileDropdown(parentLi);
150        });
151    });
152    
153    // Keep existing toggle button functionality
154    mobileDropdownToggles.forEach(function(toggle) {
155        toggle.addEventListener('click', function(e) {
156            e.preventDefault();
157            e.stopPropagation();
158            const parentLi = toggle.closest('li.has-mobile-dropdown');
159            toggleMobileDropdown(parentLi);
160        });
161
162        // Add keyboard accessibility for mobile toggles
163        toggle.addEventListener('keydown', function(e) {
164            if (e.key === 'Enter' || e.key === ' ') {
165                e.preventDefault();
166                toggle.click();
167            }
168        });
169    });
170
171    // Close desktop dropdowns when clicking outside
172    document.addEventListener('click', function(e) {
173        if (!e.target.closest('.header-nav-desktop_inner')) {
174            dropdownItems.forEach(function(item) {
175                item.classList.remove('dropdown-open');
176            });
177        }
178    });
179
180    // Close all dropdowns on window resize (mobile/desktop switch)
181    let resizeTimer;
182    window.addEventListener('resize', function() {
183        clearTimeout(resizeTimer);
184        resizeTimer = setTimeout(function() {
185            // Close desktop dropdowns
186            dropdownItems.forEach(function(item) {
187                item.classList.remove('dropdown-open');
188            });
189
190            // Close mobile dropdowns
191            mobileDropdownToggles.forEach(function(toggle) {
192                const submenu = toggle.closest('li').querySelector('.header-nav-mobile-submenu');
193                const toggleIcon = toggle.querySelector('.mobile-toggle-icon');
194                if (submenu && toggleIcon) {
195                    submenu.classList.remove('open');
196                    toggle.classList.remove('active');
197                    toggle.setAttribute('aria-expanded', 'false');
198                    toggleIcon.textContent = '+';
199                }
200            });
201        }, 250);
202    });
203});
204
205/* =========================================================
206   Sub-header: dropdowns + search + language selector (#2026-045)
207   Hover open is handled in CSS; JS adds click/touch/keyboard
208   toggles, one-open-at-a-time, outside-click/resize close, and
209   wires the language selector to Google Translate.
210   ========================================================= */
211document.addEventListener('DOMContentLoaded', function () {
212    var bar = document.querySelector('.header_sub_header');
213    if (!bar) { return; }
214
215    var dropdownItems = bar.querySelectorAll('li.has-subheader-dropdown');
216    var search = bar.querySelector('.header_sub_header_search');
217    var lang = bar.querySelector('.header_sub_header_lang');
218
219    function closeAll(except) {
220        dropdownItems.forEach(function (li) {
221            if (li !== except) {
222                li.classList.remove('is-open');
223                var a = li.querySelector('a');
224                if (a) { a.setAttribute('aria-expanded', 'false'); }
225            }
226        });
227        [search, lang].forEach(function (c) {
228            if (c && c !== except) {
229                c.classList.remove('is-open');
230                var btn = c.querySelector('button');
231                if (btn) { btn.setAttribute('aria-expanded', 'false'); }
232            }
233        });
234    }
235
236    // Nav dropdown parents — click toggles (touch + keyboard); hover handled by CSS
237    dropdownItems.forEach(function (li) {
238        var link = li.querySelector('a');
239        if (!link) { return; }
240        link.addEventListener('click', function (e) {
241            if (window.innerWidth <= 1100) { return; }
242            e.preventDefault();
243            var open = li.classList.contains('is-open');
244            closeAll(li);
245            li.classList.toggle('is-open', !open);
246            link.setAttribute('aria-expanded', String(!open));
247        });
248    });
249
250    // Search + language triggers
251    [search, lang].forEach(function (c) {
252        if (!c) { return; }
253        var btn = c.querySelector('button');
254        if (!btn) { return; }
255        btn.addEventListener('click', function (e) {
256            e.preventDefault();
257            var open = c.classList.contains('is-open');
258            closeAll(c);
259            c.classList.toggle('is-open', !open);
260            btn.setAttribute('aria-expanded', String(!open));
261        });
262    });
263
264    // Language selection -> drive Google Translate
265    function setGoogTrans(code) {
266        // GTranslate runs in "onfly" mode: it reads the googtrans cookie on load
267        // and translates the page. Set the cookie (clearing any prior value) and
268        // reload. (doGTranslate/.goog-te-combo aren't reliable here because the
269        // default widget is hidden, so its combo never populates.)
270        var host = location.hostname;
271        ['', '; domain=' + host, '; domain=.' + host].forEach(function (d) {
272            document.cookie = 'googtrans=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/' + d;
273        });
274        if (code && code !== 'en') {
275            var val = '/en/' + code;
276            document.cookie = 'googtrans=' + val + '; path=/';
277            document.cookie = 'googtrans=' + val + '; path=/; domain=.' + host;
278        }
279        location.reload();
280    }
281
282    // Bind every language option (desktop sub-header + mobile menu)
283    document.querySelectorAll('.sh-lang-option').forEach(function (opt) {
284        opt.addEventListener('click', function (e) {
285            e.preventDefault();
286            document.querySelectorAll('.sh-lang-option').forEach(function (o) {
287                o.classList.remove('is-selected');
288            });
289            opt.classList.add('is-selected');
290            if (lang) {
291                var current = lang.querySelector('.sh-lang-current');
292                if (current) { current.textContent = opt.textContent.trim(); }
293            }
294            setGoogTrans(opt.getAttribute('data-gt-lang') || 'en');
295        });
296    });
297
298    // Close on outside click, Escape, and resize
299    document.addEventListener('click', function (e) {
300        if (!e.target.closest('.header_sub_header')) { closeAll(null); }
301    });
302    document.addEventListener('keydown', function (e) {
303        if (e.key === 'Escape') { closeAll(null); }
304    });
305    var shResizeTimer;
306    window.addEventListener('resize', function () {
307        clearTimeout(shResizeTimer);
308        shResizeTimer = setTimeout(function () { closeAll(null); }, 200);
309    });
310});

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.