@@ -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
0 commit comments