diff --git a/assets/css/ie.css b/assets/css/ie.css index f70235046..83dd28fdc 100644 --- a/assets/css/ie.css +++ b/assets/css/ie.css @@ -5078,7 +5078,7 @@ h1.page-title { .primary-navigation #toggle-menu { display: none; } - .primary-navigation > .primary-menu-container > ul > li > ul { + .primary-navigation > .primary-menu-container ul > li:not(.hover) .sub-menu-toggle[aria-expanded="false"] ~ ul { display: none; } .admin-bar .primary-navigation { @@ -5114,34 +5114,58 @@ h1.page-title { margin: 13px 0; position: relative; width: 100%; - z-index: 1; -} - -.primary-navigation > div > .menu-wrapper li:hover, .primary-navigation > div > .menu-wrapper li:focus-within { - cursor: pointer; - z-index: 99999; } @media only screen and (min-width: 482px) { .primary-navigation > div > .menu-wrapper li { - display: inherit; margin: 0; width: inherit; - /* Submenu display in the responsive menu */ - } - .primary-navigation > div > .menu-wrapper li:hover > ul, - .primary-navigation > div > .menu-wrapper li:focus-within > ul, - .primary-navigation > div > .menu-wrapper li ul:hover, - .primary-navigation > div > .menu-wrapper li ul:focus { - visibility: visible; - opacity: 1; - display: block; } .primary-navigation > div > .menu-wrapper li:last-child { margin-right: 0; } } +.primary-navigation > div > .menu-wrapper .sub-menu-toggle { + display: flex; + height: 44px; + width: 44px; + padding: 0; + justify-content: center; + align-items: center; + background: transparent; + color: currentColor; + border: none; +} + +.primary-navigation > div > .menu-wrapper .sub-menu-toggle:focus { + outline: 2px solid #28303d; +} + +@media only screen and (max-width: 481px) { + .primary-navigation > div > .menu-wrapper .sub-menu-toggle { + display: none; + } +} + +.primary-navigation > div > .menu-wrapper .sub-menu-toggle[aria-expanded="false"] { + /* stylelint-disable */ + background: url("data:image/svg+xml;utf8,") no-repeat center center; + /* stylelint-enable */ +} + +.primary-navigation > div > .menu-wrapper .sub-menu-toggle[aria-expanded="true"] { + /* stylelint-disable */ + background: url("data:image/svg+xml;utf8,") no-repeat center center; + /* stylelint-enable */ +} + +.primary-navigation > div > .menu-wrapper .hover .sub-menu-toggle { + /* stylelint-disable */ + background: url("data:image/svg+xml;utf8,") no-repeat center center; + /* stylelint-enable */ +} + .primary-navigation > div > .menu-wrapper > li > .sub-menu { position: relative; } @@ -5152,11 +5176,10 @@ h1.page-title { left: 0; margin: 0; min-width: max-content; - opacity: 0; position: absolute; - top: calc(100% - 13px); + top: 100%; transition: all 0.5s ease; - visibility: hidden; + z-index: 99999; } } diff --git a/assets/js/polyfills.js b/assets/js/polyfills.js new file mode 100644 index 000000000..0fe666153 --- /dev/null +++ b/assets/js/polyfills.js @@ -0,0 +1,41 @@ +/** + * File primary-navigation.js. + * + * Required to open and close the mobile navigation. + */ + +/** + * Polyfill for Element.closest() because we need to support IE11. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/closest + */ +if ( ! Element.prototype.matches ) { + Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; +} + +if ( ! Element.prototype.closest ) { + Element.prototype.closest = function( s ) { + var el = this; + do { + if ( Element.prototype.matches.call( el, s ) ) { + return el; + } + el = el.parentElement || el.parentNode; + } while ( el !== null && el.nodeType === 1 ); + return null; + }; +} + +/** + * Polyfill for NodeList.foreach() because we need to support IE11. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach + */ +if ( window.NodeList && ! NodeList.prototype.forEach ) { + NodeList.prototype.forEach = function( callback, thisArg ) { + thisArg = thisArg || window; + for ( var i = 0; i < this.length; i++ ) { + callback.call( thisArg, this[i], i, this ); + } + }; +} diff --git a/assets/js/primary-navigation.js b/assets/js/primary-navigation.js index 44365ce9e..5ea77926d 100644 --- a/assets/js/primary-navigation.js +++ b/assets/js/primary-navigation.js @@ -3,30 +3,63 @@ * * Required to open and close the mobile navigation. */ -( function() { - /** - * Toggle an attribute's value - * - * @param {Element} element - * @param {Attribute} attribute - * @param {String} trueVal - * @param {String} falseVal - * @since 1.0.0 - */ - var toggleAttribute = function ( element, attribute, trueVal, falseVal ) { - if ( undefined === trueVal ) { - trueVal = true; - } - if ( undefined === falseVal ) { - falseVal = false; +/** + * Toggle an attribute's value + * + * @param {Element} el - The element. + * @since 1.0.0 + */ +function twentytwentyoneToggleAriaExpanded( el, withListeners ) { + if ( 'true' !== el.getAttribute( 'aria-expanded' ) ) { + el.setAttribute( 'aria-expanded', 'true' ); + if ( withListeners ) { + document.addEventListener( 'click', twentytwentyoneCollapseMenuOnClickOutside ); } - if ( trueVal !== element.getAttribute( attribute ) ) { - element.setAttribute( attribute, trueVal ); - } else { - element.setAttribute( attribute, falseVal ); + } else { + el.setAttribute( 'aria-expanded', 'false' ); + if ( withListeners ) { + document.removeEventListener( 'click', twentytwentyoneCollapseMenuOnClickOutside ); } } +} + +function twentytwentyoneCollapseMenuOnClickOutside( event ) { + if ( ! document.getElementById( 'site-navigation' ).contains( event.target ) ) { + document.getElementById( 'site-navigation' ).querySelectorAll( '.sub-menu-toggle' ).forEach( function( button ) { + button.setAttribute( 'aria-expanded', 'false' ); + } ); + } +} + +/** + * Handle clicks on submenu toggles. + * + * @param {Element} el - The element. + */ +function twentytwentyoneExpandSubMenu( el ) { + + // Close other expanded items. + el.closest( 'nav' ).querySelectorAll( '.sub-menu-toggle' ).forEach( function( button ) { + if ( button !== el ) { + button.setAttribute( 'aria-expanded', 'false' ); + } + } ); + + // Toggle aria-expanded on the button. + twentytwentyoneToggleAriaExpanded( el, true ); + + // On tab-away collapse the menu. + el.parentNode.querySelectorAll( 'ul > li:last-child > a' ).forEach( function( linkEl ) { + linkEl.addEventListener( 'blur', function( event ) { + if ( ! el.parentNode.contains( event.relatedTarget ) ) { + el.setAttribute( 'aria-expanded', 'false' ); + } + } ); + } ); +} + +( function() { /** * Menu Toggle Behaviors @@ -34,27 +67,26 @@ * @param {Element} element */ var navMenu = function ( id ){ - var wrapper = document.body; // this is the element to which a CSS class is added when a mobile nav menu is open - var mobileButton = document.getElementById( `${ id }-mobile-menu` ); + var wrapper = document.body, // this is the element to which a CSS class is added when a mobile nav menu is open + mobileButton = document.getElementById( `${ id }-mobile-menu` ); if ( mobileButton ){ mobileButton.onclick = function() { wrapper.classList.toggle( `${ id }-navigation-open` ); wrapper.classList.toggle( 'lock-scrolling' ); - toggleAttribute( mobileButton, 'aria-expanded', 'true', 'false' ); + twentytwentyoneToggleAriaExpanded( mobileButton ); mobileButton.focus(); } } - /** * Trap keyboard navigation in the menu modal. * Adapted from TwentyTwenty */ document.addEventListener( 'keydown', function( event ) { + var modal, elements, selectors, lastEl, firstEl, activeEl, tabKey, shiftKey, escKey; if ( ! wrapper.classList.contains( `${ id }-navigation-open` ) ){ return; - } - var modal, elements, selectors, lastEl, firstEl, activeEl, tabKey, shiftKey, escKey; + } modal = document.querySelector( `.${ id }-navigation` ); selectors = "input, a, button"; @@ -70,7 +102,7 @@ if ( escKey ) { event.preventDefault(); wrapper.classList.remove( `${ id }-navigation-open`, 'lock-scrolling' ); - toggleAttribute( mobileButton, 'aria-expanded', 'true', 'false' ); + twentytwentyoneToggleAriaExpanded( mobileButton ); mobileButton.focus(); } @@ -88,10 +120,19 @@ if ( tabKey && firstEl === lastEl ) { event.preventDefault(); } - }); + } ); + + document.getElementById( 'site-navigation' ).querySelectorAll( '.menu-wrapper > .menu-item-has-children' ).forEach( function( li ) { + li.addEventListener( 'mouseenter', function() { + this.classList.add( 'hover' ); + } ); + li.addEventListener( 'mouseleave', function() { + this.classList.remove( 'hover' ); + } ); + } ); } window.addEventListener( 'load', function() { new navMenu( 'primary' ); }); -} )(); \ No newline at end of file +} )(); diff --git a/assets/sass/06-components/navigation.scss b/assets/sass/06-components/navigation.scss index 5c25498b9..5230a9725 100644 --- a/assets/sass/06-components/navigation.scss +++ b/assets/sass/06-components/navigation.scss @@ -162,7 +162,7 @@ } // Hide sub-sub-menus - > .primary-menu-container > ul > li > ul { + > .primary-menu-container ul > li:not(.hover) .sub-menu-toggle[aria-expanded="false"] ~ ul { display: none; } @@ -200,35 +200,59 @@ margin: var(--primary-nav--padding) 0; position: relative; width: 100%; - z-index: 1; - - &:hover, - &:focus-within { - cursor: pointer; - z-index: 99999; - } @include media(mobile) { - display: inherit; margin: 0; width: inherit; - /* Submenu display in the responsive menu */ - &:hover > ul, - &:focus-within > ul, - ul:hover, - ul:focus { - visibility: visible; - opacity: 1; - display: block; - } - &:last-child() { margin-right: 0; } } } + // Sub-menu buttons + .sub-menu-toggle { + display: flex; + height: 44px; + width: 44px; + padding: 0; + justify-content: center; + align-items: center; + background: transparent; + color: currentColor; + border: none; + + &:focus { + outline: 2px solid var(--wp--style--color--link, var(--global--color-primary)); + } + + @include media(mobile-only) { + display: none; + } + + // When the sub-menu is closed, display the plus icon + &[aria-expanded="false"] { + /* stylelint-disable */ + background: url("data:image/svg+xml;utf8,") no-repeat center center; + /* stylelint-enable */ + } + + // When the sub-menu is open, display the minus icon + &[aria-expanded="true"] { + /* stylelint-disable */ + background: url("data:image/svg+xml;utf8,") no-repeat center center; + /* stylelint-enable */ + } + + } + + .hover .sub-menu-toggle { + /* stylelint-disable */ + background: url("data:image/svg+xml;utf8,") no-repeat center center; + /* stylelint-enable */ + } + // Sub-menus Flyout > li > .sub-menu { @@ -239,11 +263,10 @@ left: 0; margin: 0; min-width: max-content; - opacity: 0; position: absolute; - top: calc(100% - var(--primary-nav--padding)); + top: 100%; transition: all 0.5s ease; - visibility: hidden; + z-index: 99999; } .sub-menu { @@ -315,7 +338,7 @@ list-style: none; margin-left: var(--primary-nav--padding); - // Sub menu items om wide screens. + // Sub-menu items om wide screens. @include media(mobile) { padding: 0 var(--primary-nav--padding); diff --git a/classes/class-twenty-twenty-one-svg-icons.php b/classes/class-twenty-twenty-one-svg-icons.php index 57c5b4dbb..69eeb28e9 100644 --- a/classes/class-twenty-twenty-one-svg-icons.php +++ b/classes/class-twenty-twenty-one-svg-icons.php @@ -62,7 +62,5 @@ public static function get_svg( $group, $icon, $size ) { 'arrow_left' => '', 'close' => '', 'menu' => '', - 'minus' => '', - 'plus' => '', ); } diff --git a/functions.php b/functions.php index a37f91f7e..426aa5fb8 100644 --- a/functions.php +++ b/functions.php @@ -350,7 +350,20 @@ function twenty_twenty_one_scripts() { // Main navigation scripts. if ( has_nav_menu( 'primary' ) ) { - wp_enqueue_script( 'twenty-twenty-one-primary-navigation-script', get_template_directory_uri() . '/assets/js/primary-navigation.js', array(), wp_get_theme()->get( 'Version' ), true ); + wp_register_script( + 'twenty-twenty-one-ie11-polyfills', + get_template_directory_uri() . '/assets/js/polyfills.js', + array(), + wp_get_theme()->get( 'Version' ), + true + ); + wp_enqueue_script( + 'twenty-twenty-one-primary-navigation-script', + get_template_directory_uri() . '/assets/js/primary-navigation.js', + array( 'twenty-twenty-one-ie11-polyfills' ), + wp_get_theme()->get( 'Version' ), + true + ); } } add_action( 'wp_enqueue_scripts', 'twenty_twenty_one_scripts' ); diff --git a/header.php b/header.php index 1208ee030..6b8e7472b 100644 --- a/header.php +++ b/header.php @@ -30,9 +30,9 @@ -