Skip to content

Commit 19c08e3

Browse files
committed
Bundled Themes: A11y: Dismiss submenus with esc in Twenty Twenty.
Allow submenus in Twenty Twenty to be dismissed using the `esc` key to meet WCAG 1.4.13. Set focus to previous submenu parent on `esc`, and ensure that after escaping the last submenu, tab moves to the next parent item, not back into the submenu. Props lcarevic, rcreators, pratiklondhe, poena, karmatosed, chaion07, audrasjb, mehdi01, mohonchandra, najmulsaju, saurabhdhariwal, ugyensupport, shailu25. Fixes #49950. git-svn-id: https://develop.svn.wordpress.org/trunk@60040 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 10e18b6 commit 19c08e3

3 files changed

Lines changed: 84 additions & 14 deletions

File tree

src/wp-content/themes/twentytwenty/assets/js/index.js

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -438,29 +438,91 @@ twentytwenty.primaryMenu = {
438438

439439
links = menu.getElementsByTagName( 'a' );
440440

441-
// Each time a menu link is focused or blurred, toggle focus.
441+
// Each time a menu link is focused, update focus.
442442
for ( i = 0, len = links.length; i < len; i++ ) {
443-
links[i].addEventListener( 'focus', toggleFocus, true );
444-
links[i].addEventListener( 'blur', toggleFocus, true );
443+
links[i].addEventListener( 'focus', updateFocus, true );
445444
}
446445

447-
//Sets or removes the .focus class on an element.
448-
function toggleFocus() {
446+
menu.addEventListener( 'focusout', removeFocus, true );
447+
448+
// Remove focus classes from menu.
449+
function removeFocus(e){
450+
const leavingMenu = ! menu.contains( e.relatedTarget );
451+
452+
if ( leavingMenu ) {
453+
// Remove focus from all li elements of primary-menu.
454+
menu.querySelectorAll( 'li' ).forEach( function( el ) {
455+
if ( el.classList.contains( 'focus' ) ) {
456+
el.classList.remove( 'focus', 'closed' );
457+
}
458+
});
459+
}
460+
}
461+
462+
// Update focus class on an element.
463+
function updateFocus() {
449464
var self = this;
450465

451-
// Move up through the ancestors of the current link until we hit .primary-menu.
452-
while ( -1 === self.className.indexOf( 'primary-menu' ) ) {
453-
// On li elements toggle the class .focus.
454-
if ( 'li' === self.tagName.toLowerCase() ) {
455-
if ( -1 !== self.className.indexOf( 'focus' ) ) {
456-
self.className = self.className.replace( ' focus', '' );
466+
// Remove focus from all li elements of primary-menu.
467+
menu.querySelectorAll( 'li' ).forEach( function( el ){
468+
if ( el.classList.contains( 'closed' ) ) {
469+
el.classList.remove( 'closed' );
470+
}
471+
if ( el.classList.contains( 'focus' ) ) {
472+
el.classList.remove( 'focus' );
473+
}
474+
});
475+
476+
// Set focus on current `a` element's parent `li`.
477+
self.parentElement.classList.add( 'focus' );
478+
// If current element is inside sub-menu find main parent li and add focus.
479+
if ( self.closest( '.menu-item-has-children' ) ) {
480+
twentytwentyFindParents( self, 'li.menu-item-has-children' ).forEach( function( element ) {
481+
element.classList.add( 'focus' );
482+
});
483+
}
484+
}
485+
486+
// When the `esc` key is pressed while in menu, move focus up one level.
487+
menu.addEventListener( 'keydown', removeFocusEsc, true );
488+
489+
// Remove focus when `esc` key pressed.
490+
function removeFocusEsc( e ) {
491+
e = e || window.event;
492+
var isEscape = false,
493+
focusedElement = e.target;
494+
495+
// Find if pressed key is `esc`.
496+
if ( 'key' in e ) {
497+
isEscape = ( e.key === 'Escape' || e.key === 'Esc' );
498+
} else {
499+
isEscape = ( e.keyCode === 27 );
500+
}
501+
502+
// If pressed key is esc, remove focus class from parent menu li.
503+
if ( isEscape ) {
504+
var parentLi = focusedElement.closest( 'li' ),
505+
nestedParent = closestExcludingSelf( parentLi, 'li.menu-item-has-children' ),
506+
focusPosition = nestedParent ? nestedParent.querySelector('a') : false;
507+
508+
if ( null !== nestedParent ) {
509+
nestedParent.classList.add( 'focus' );
510+
focusPosition.focus();
457511
} else {
458-
self.className += ' focus';
512+
parentLi.classList.remove( 'focus' );
513+
parentLi.classList.add( 'closed' );
459514
}
460-
}
461-
self = self.parentElement;
462515
}
463516
}
517+
518+
function closestExcludingSelf(element, selector) {
519+
if ( ! element || ! selector ) {
520+
return null;
521+
}
522+
const parent = element.parentElement;
523+
524+
return parent ? parent.closest(selector) : null;
525+
}
464526
}
465527
}; // twentytwenty.primaryMenu
466528

src/wp-content/themes/twentytwenty/style-rtl.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,6 +1676,10 @@ ul.primary-menu {
16761676
z-index: 1;
16771677
}
16781678

1679+
.primary-menu .closed ul {
1680+
display: none;
1681+
}
1682+
16791683
.primary-menu li.menu-item-has-children:hover > ul,
16801684
.primary-menu li.menu-item-has-children:focus > ul,
16811685
.primary-menu li.menu-item-has-children.focus > ul {

src/wp-content/themes/twentytwenty/style.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1682,6 +1682,10 @@ ul.primary-menu {
16821682
z-index: 1;
16831683
}
16841684

1685+
.primary-menu .closed ul {
1686+
display: none;
1687+
}
1688+
16851689
.primary-menu li.menu-item-has-children:hover > ul,
16861690
.primary-menu li.menu-item-has-children:focus > ul,
16871691
.primary-menu li.menu-item-has-children.focus > ul {

0 commit comments

Comments
 (0)