PageSourceSearch

https://www.freiham.de/wp-content/themes/twentytwentyone-child/primary-navigation.js?ver=1.0.1

js freiham.de collected 2026-10-08 17:09:51 UTC 6,549 bytes, 223 lines download raw bytes

1/**
2 * File primary-navigation.js.
3 *
4 * Required to open and close the mobile navigation.
5 */
6
7/**
8 * Toggles an attribute's value
9 *
10 * @since Twenty Twenty-One 1.0
11 *
12 * @param {Element} el - The element.
13 * @param {boolean} withListeners - Whether we want to add/remove listeners or not.
14 */
15function twentytwentyoneToggleAriaExpanded(el, withListeners) {
16	if ('true' !== el.getAttribute('aria-expanded')) {
17		el.setAttribute('aria-expanded', 'true');
18		twentytwentyoneSubmenuPosition(el.parentElement);
19		if (withListeners) {
20			document.addEventListener('click', twentytwentyoneCollapseMenuOnClickOutside);
21		}
22	} else {
23		el.setAttribute('aria-expanded', 'false');
24		if (withListeners) {
25			document.removeEventListener('click', twentytwentyoneCollapseMenuOnClickOutside);
26		}
27	}
28}
29
30function twentytwentyoneCollapseMenuOnClickOutside(event) {
31	if (!document.getElementById('site-navigation').contains(event.target)) {
32		document.getElementById('site-navigation').querySelectorAll('.sub-menu-toggle').forEach(function (button) {
33			button.setAttribute('aria-expanded', 'false');
34		});
35	}
36}
37
38/**
39 * Changes the position of submenus so they always fit the screen horizontally.
40 *
41 * @since Twenty Twenty-One 1.0
42 *
43 * @param {Element} li - The li element.
44 */
45function twentytwentyoneSubmenuPosition(li) {
46	var subMenu = li.querySelector('ul.sub-menu'),
47		rect,
48		right,
49		left,
50		windowWidth;
51
52	if (!subMenu) {
53		return;
54	}
55
56	rect = subMenu.getBoundingClientRect();
57	right = Math.round(rect.right);
58	left = Math.round(rect.left);
59	windowWidth = Math.round(window.innerWidth);
60
61	if (right > windowWidth) {
62		subMenu.classList.add('submenu-reposition-right');
63	} else if (document.body.classList.contains('rtl') && left < 0) {
64		subMenu.classList.add('submenu-reposition-left');
65	}
66}
67
68/**
69 * Handles clicks on submenu toggles.
70 *
71 * @since Twenty Twenty-One 1.0
72 *
73 * @param {Element} el - The element.
74 */
75function twentytwentyoneExpandSubMenu(el) { // jshint ignore:line
76	// Close other expanded items.
77	el.closest('nav').querySelectorAll('.sub-menu-toggle').forEach(function (button) {
78		if (button !== el) {
79			button.setAttribute('aria-expanded', 'false');
80		}
81	});
82
83	// Toggle aria-expanded on the button.
84	twentytwentyoneToggleAriaExpanded(el, true);
85
86	// On tab-away collapse the menu.
87	el.parentNode.querySelectorAll('ul > li:last-child > a').forEach(function (linkEl) {
88		linkEl.addEventListener('blur', function (event) {
89			if (!el.parentNode.contains(event.relatedTarget)) {
90				el.setAttribute('aria-expanded', 'false');
91			}
92		});
93	});
94}
95
96(function () {
97	/**
98	 * Menu Toggle Behaviors
99	 *
100	 * @since Twenty Twenty-One 1.0
101	 *
102	 * @param {string} id - The ID.
103	 */
104	var navMenu = function (id) {
105		var wrapper = document.body, // this is the element to which a CSS class is added when a mobile nav menu is open
106			mobileButton = document.getElementById(id + '-mobile-menu'),
107			navMenuEl = document.getElementById('site-navigation');
108
109		// If there's no nav menu, none of this is necessary.
110		if (!navMenuEl) {
111			return;
112		}
113
114		if (mobileButton) {
115			mobileButton.onclick = function () {
116				wrapper.classList.toggle(id + '-navigation-open');
117				wrapper.classList.toggle('lock-scrolling');
118				twentytwentyoneToggleAriaExpanded(mobileButton);
119				mobileButton.focus();
120			};
121		}
122
123		// Add aria-controls attributes to primary sub-menu.
124		var subMenus = document.querySelectorAll('.primary-menu-container .sub-menu');
125		subMenus.forEach(function (subMenu, index) {
126			var parentLi = subMenu.closest('li.menu-item-has-children');
127			subMenu.id = 'sub-menu-' + (index + 1);
128			if (parentLi) {
129				var parentLink = parentLi.querySelector('button');
130				if (parentLink) {
131					parentLink.setAttribute('aria-controls', subMenu.id);
132				}
133			}
134		});
135
136		/**
137		 * Traps keyboard navigation in the menu modal.
138		 * Adapted from Twenty Twenty.
139		 *
140		 * @since Twenty Twenty-One 1.0
141		 */
142		document.addEventListener('keydown', function (event) {
143			var modal, elements, selectors, lastEl, firstEl, activeEl, tabKey, shift
143Key, escKey;
144			if (!wrapper.classList.contains(id + '-navigation-open')) {
145				return;
146			}
147
148			modal = document.querySelector('.' + id + '-navigation');
149			selectors = 'input, a, button';
150			elements = modal.querySelectorAll(selectors);
151			elements = Array.prototype.slice.call(elements);
152			tabKey = event.keyCode === 9;
153			shiftKey = event.shiftKey;
154			escKey = event.keyCode === 27;
155			activeEl = document.activeElement; // eslint-disable-line @wordpress/no-global-active-element
156			lastEl = elements[elements.length - 1];
157			firstEl = elements[0];
158
159			if (escKey) {
160				event.preventDefault();
161				wrapper.classList.remove(id + '-navigation-open', 'lock-scrolling');
162				twentytwentyoneToggleAriaExpanded(mobileButton);
163				mobileButton.focus();
164			}
165
166			if (!shiftKey && tabKey && lastEl === activeEl) {
167				event.preventDefault();
168				firstEl.focus();
169			}
170
171			if (shiftKey && tabKey && firstEl === activeEl) {
172				event.preventDefault();
173				lastEl.focus();
174			}
175
176			// If there are no elements in the menu, don't move the focus
177			if (tabKey && firstEl === lastEl) {
178				event.preventDefault();
179			}
180		});
181
182		/**
183		 * Closes menu and scrolls to anchor when an anchor link is clicked.
184		 * Adapted from Twenty Twenty.
185		 *
186		 * @since Twenty Twenty-One 1.1
187		 */
188		document.getElementById('site-navigation').addEventListener('click', function (event) {
189			// If target onclick is <a> with # within the href attribute
190			if (event.target.hash) {
191				wrapper.classList.remove(id + '-navigation-open', 'lock-scrolling');
192				twentytwentyoneToggleAriaExpanded(mobileButton);
193				// Wait 550 and scroll to the anchor.
194				setTimeout(function () {
195					var anchor = document.getElementById(event.target.hash.slice(1));
196					if (anchor) {
197						anchor.scrollIntoView();
198					}
199				}, 550);
200			}
201		});
202
203		navMenuEl.querySelectorAll('.menu-wrapper > .menu-item-has-children').forEach(function (li) {
204			li.addEventListener('mouseenter', function () {
205				var toggle = this.querySelector('.sub-menu-toggle');
206				if (toggle && typeof toggle.setAttribute === 'function') {
207					toggle.setAttribute('aria-expanded', 'true');
208				}
209				twentytwentyoneSubmenuPosition(li);
210			});
211			li.addEventListener('mouseleave', function () {
212				var toggle = this.querySelector('.sub-menu-toggle');
213				if (toggle && typeof toggle.setAttribute === 'function') {
214					toggle.setAttribute('aria-expanded', 'false');
215				}
216			});
217		});
218	};
219
220	document.addEventListener('DOMContentLoaded', function () {
221		new navMenu('primary');
222	});
223}());

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.