PageSourceSearch

https://www.atomicmusicgroup.com/wp-content/themes/twentytwentyone/assets/js/primary-navigation.js?ver=2.9

js atomicmusicgroup.com collected 2026-09-25 01:23:02 UTC 6,545 bytes, 217 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				this.querySelector( '.sub-menu-toggle' ).setAttribute( 'aria-expanded', 'true' );
206				twentytwentyoneSubmenuPosition( li );
207			} );
208			li.addEventListener( 'mouseleave', function() {
209				this.querySelector( '.sub-menu-toggle' ).setAttribute( 'aria-expanded', 'false' );
210			} );
211		} );
212	};
213
214	document.addEventListener( 'DOMContentLoaded', function() {
215		new navMenu( 'primary' );
216	} );
217}() );

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.