PageSourceSearch

https://www.veloforum.org.ua/wp-content/themes/twentytwentyone/assets/js/primary-navigation.js?ver=1.0.0

js veloforum.org.ua collected 2026-10-07 03:06:46 UTC 5,705 bytes, 184 lines download raw bytes

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

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.