PageSourceSearch

https://reactivezero.com/js/mobile-nav.js?v=20260915-phone

js reactivezero.com collected 2026-09-26 14:38:40 UTC 5,048 bytes, 118 lines download raw bytes

1/* ReactiveZero mobile navigation.
2   Injects a hamburger button, turns the navbar menu into a slide-down
3   panel, and makes the Solutions/Industries/Resources dropdowns work as
4   tap-to-expand accordions on phones & tablets. Desktop is untouched. */
5(function () {
6  'use strict';
7
8  var DESKTOP = 1280; /* matches the CSS ≤1279px mobile breakpoint */
9
10  var MENU_ICON =
11    '<svg class="rz-ic-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">' +
12    '<path d="M3.5 6.5h17M3.5 12h17M3.5 17.5h17"/></svg>';
13  var CLOSE_ICON =
14    '<svg class="rz-ic-close" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">' +
15    '<path d="M5.5 5.5l13 13M18.5 5.5l-13 13"/></svg>';
16
17  function init() {
18    var comp = document.querySelector('.navbar2_component');
19    var container = document.querySelector('.navbar2_container');
20    var menu = document.querySelector('.navbar2_menu');
21    if (!comp || !container || !menu || document.querySelector('.rz-nav-toggle')) return;
22
23    var btn = document.createElement('button');
24    btn.type = 'button';
25    btn.className = 'rz-nav-toggle';
26    btn.setAttribute('aria-label', 'Open menu');
27    btn.setAttribute('aria-expanded', 'false');
28    btn.innerHTML = MENU_ICON + CLOSE_ICON;
29    container.appendChild(btn);
30
31    function isMobile() { return window.innerWidth < DESKTOP; }
32
33    function closeAccordions() {
34      menu.querySelectorAll('.navbar2_menu-dropdown.rz-open').forEach(function (d) {
35        d.classList.remove('rz-open');
36      });
37    }
38
39    function open() {
40      comp.classList.add('rz-nav-open');
41      btn.setAttribute('aria-expanded', 'true');
42      btn.setAttribute('aria-label', 'Close menu');
43      document.body.classList.add('rz-nav-locked');
44    }
45    function close() {
46      comp.classList.remove('rz-nav-open');
47      btn.setAttribute('aria-expanded', 'false');
48      btn.setAttribute('aria-label', 'Open menu');
49      document.body.classList.remove('rz-nav-locked');
50      closeAccordions();
51    }
52    function toggle() { comp.classList.contains('rz-nav-open') ? close() : open(); }
53
54    btn.addEventListener('click', function (e) { e.stopPropagation(); toggle(); });
55
56    /* Dropdown toggles → accordion on mobile (desktop keeps hover) */
57    menu.querySelectorAll('.navbar2_dropdwn-toggle, .w-dropdown-toggle').forEach(function (tg) {
58      tg.addEventListener('click', function (e) {
59        if (!isMobile()) return;
60        e.preventDefault();
61        e.stopPropagation();
62        var dd = tg.closest('.navbar2_menu-dropdown') || tg.closest('.w-dropdown');
63        if (!dd) return;
64        var willOpen = !dd.classList.contains('rz-open');
65        menu.querySelectorAll('.navbar2_menu-dropdown.rz-open').forEach(function (o) {
66          if (o !== dd) o.classList.remove('rz-open');
67        });
68        dd.classList.toggle('rz-open', willOpen);
69      }, true);
70    });
71
72    /* Tapping a real navigation link closes the panel */
73    menu.querySelectorAll('a.navbar2_link, a.navbar2_dropdown-link').forEach(function (a) {
74      a.addEventListener('click', function () { if (isMobile()) close(); });
75    });
76
77    /* Desktop pointer hygiene. The desktop CSS keeps a dropdown open while it is
78       :hover OR :focus-within (the latter so keyboard users can open it). A mouse
79       click on the toggle, or on a link that opens a new tab (the R0 link), would
80       otherwise leave that element focused and pin the menu open after the pointer
81       leaves. So on desktop: mouse clicks never focus these elements, and on
82       mouseleave any non-keyboard focus inside the dropdown is dropped. Keyboard
83       (:focus-visible) focus is left alone. */
84    menu.querySelectorAll('.navbar2_dropdwn-toggle, .w-dropdown-toggle, a.navbar2_dropdown-link').forEach(function (el) {
85      el.addEventListener('mousedown', function (e) { if (!isMobile()) e.preventDefault(); });
86    });
87    menu.querySelectorAll('.navbar2_menu-dropdown, .w-dropdown').forEach(function (dd) {
88      dd.addEventListener('mouseleave', function () {
89        if (isMobile()) return;
90        var ae = document.activeElement;
91        if (!ae || !dd.contains(ae)) return;
92        var kb = false; try { kb = ae.matches(':focus-visible'); } catch (x) {}
93        if (!kb) ae.blur();
94      });
95    });
96
97    /* Dismiss on outside click / Escape */
98    document.addEventListener('click', function (e) {
99      if (comp.classList.contains('rz-nav-open') && !comp.contains(e.target)) close();
100    });
101    document.addEventListener('keydown', function (e) {
102      if (e.key === 'Escape' && comp.classList.contains('rz-nav-open')) { close(); btn.focus(); }
103    });
104
105    /* Reset when growing back to desktop */
106    var raf;
107    window.addEventListener('resize', function () {
108      cancelAnimationFrame(raf);
109      raf = requestAnimationFrame(function () { if (!isMobile()) close(); });
110    });
111  }
112
113  if (document.readyState === 'loading') {
114    document.addEventListener('DOMContentLoaded', init);
115  } else {
116    init();
117  }
118})();

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.