PageSourceSearch

https://www.zvox.ai/blog/wp-content/themes/hello-elementor-child/assets/js/header.js?ver=1789712845

js zvox.ai collected 2026-10-05 13:21:24 UTC 5,628 bytes, 134 lines download raw bytes

1/*
2 * Site header behaviour, 1:1 with zvox.ai (components/New/ui/NewHeader.js).
3 * The markup is in header.php and every visual state is a class styled in
4 * assets/css/header.css:
5 *
6 *   .is-raised     past 8px of scroll, or while the menu is open
7 *   .is-menu-open  mobile panel + scrim (below 1280px)
8 *   .is-closing    the panel's short exit before it is hidden
9 *   .zx-group.is-open, .zx-drawer.is-open
10 *
11 * While the menu is open the page stops scrolling and `app:lock-scroll` is
12 * dispatched, which the overlay scroll indicator listens for - the same event
13 * the site uses.
14 */
15(function () {
16    'use strict';
17
18    function init() {
19        var header = document.getElementById('zxHeader');
20        if (!header) return;
21
22        var burger = document.getElementById('zxBurger');
23        var scrim = document.getElementById('zxScrim');
24        var menuOpen = false;
25        var closeTimer = null;
26        var previousOverflow = '';
27
28        // ---- Scroll: lift into the strip -------------------------------
29        function syncRaised() {
30            header.classList.toggle('is-raised', menuOpen || window.scrollY > 8);
31        }
32        window.addEventListener('scroll', syncRaised, { passive: true });
33        syncRaised();
34
35        // ---- Desktop dropdowns: hover, plus keyboard/focus -------------
36        var groups = header.querySelectorAll('[data-zx-group]');
37        groups.forEach(function (group) {
38            var trigger = group.querySelector('button');
39
40            function setOpen(open) {
41                group.classList.toggle('is-open', open);
42                if (trigger) trigger.setAttribute('aria-expanded', open ? 'true' : 'false');
43            }
44
45            group.addEventListener('mouseenter', function () { setOpen(true); });
46            group.addEventListener('mouseleave', function () { setOpen(false); });
47            group.addEventListener('focusin', function () { setOpen(true); });
48            group.addEventListener('focusout', function (event) {
49                if (!group.contains(event.relatedTarget)) setOpen(false);
50            });
51            if (trigger) {
52                trigger.addEventListener('click', function () {
53                    setOpen(!group.classList.contains('is-open'));
54                });
55            }
56        });
57
58        // ---- Mobile menu -------------------------------------------------
59        function onKey(event) {
60            if (event.key === 'Escape') setMenu(false);
61        }
62
63        function setMenu(open) {
64            if (open === menuOpen) return;
65            menuOpen = open;
66            window.clearTimeout(closeTimer);
67
68            if (open) {
69                header.classList.remove('is-closing');
70                header.classList.add('is-menu-open');
71                previousOverflow = document.body.style.overflow;
72                document.body.style.overflow = 'hidden';
73                if (window.lenis && window.lenis.stop) window.lenis.stop();
74                window.addEventListener('keydown', onKey);
75                window.dispatchEvent(new CustomEvent('app:lock-scroll'));
76            } else {
77                header.classList.remove('is-menu-open');
78                // Everything leaves at once and fast, as on the site.
79                header.classList.add('is-closing');
80                closeTimer = window.setTimeout(function () {
81                    header.classList.remove('is-closing');
82                }, 160);
83                document.body.style.overflow = previousOverflow;
84                if (window.lenis && window.lenis.start) window.lenis.start();
85                window.removeEventListener('keydown', onKey);
86                window.dispatchEvent(new CustomEvent('app:unlock-scroll'));
87            }
88
89            if (burger) {
90                burger.setAttribute('aria-expanded', open ? 'true' : 'false');
91                burger.setAttribute('aria-label', open ? 'Close menu' : 'Open menu');
92            }
93            syncRaised();
94        }
95
96        if (burger) {
97            burger.addEventListener('click', function () { setMenu(!menuOpen); });
98        }
99        if (scrim) {
100            scrim.addEventListener('click', function () { setMenu(false); });
101        }
102
103        // Growing past the breakpoint with the menu open would leave the page
104        // locked behind a panel that CSS has hidden.
105        if (window.matchMedia) {
106            var desktop = window.matchMedia('(min-width: 1280px)');
107            var onBreakpoint = function () { if (desktop.matches) setMenu(false); };
108            if (desktop.addEventListener) desktop.addEventListener('change', onBreakpoint);
109            else if (desktop.addListener) desktop.addListener(onBreakpoint);
110        }
111
112        // Leaving the page from inside the menu.
113        header.querySelectorAll('.zx-panel a').forEach(function (link) {
114            link.addEventListener('click', function () { setMenu(false); });
115        });
116
117        // ---- Drawers (Solutions / Company) ------------------------------
118        header.querySelectorAll('.zx-drawer-toggle').forEach(function (toggle) {
119            var drawer = document.getElementById(toggle.getAttribute('aria-controls'));
120            if (!drawer) return;
121            toggle.addEventListener('click', function () {
122                var open = !drawer.classList.contains('is-open');
123                drawer.classList.toggle('is-open', open);
124                toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
125            });
126        });
127    }
128
129    if (document.readyState === 'loading') {
130        document.addEventListener('DOMContentLoaded', init);
131    } else {
132        init();
133    }
134})();

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.