Skip to content

Commit 22b1a4d

Browse files
authored
a11y menu issue when focus moves away (#284)
* Close desktop submenu when focus moves away * Further menu accessibility improvements - Close the mobile menu when focus moves away from the last item at the top level - Improve the functionality for closing the desktop menu when focus moves away from the last item * fix mobile menu test * Move open and close functionality to their own functions
1 parent ba7fe97 commit 22b1a4d

File tree

6 files changed

+57
-16
lines changed

6 files changed

+57
-16
lines changed

tbx/project_styleguide/templates/patterns/navigation/components/includes/subnav-child-menu.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<ul class="sub-nav-desktop__list sub-nav-desktop__list--child">
55
{% for page in pages %}
66
<li class="sub-nav-desktop__item">
7-
<a class="sub-nav-desktop__link sub-nav-desktop__link--child sub-nav-desktop__link--no-children" href="{% pageurl page %}">
7+
<a class="sub-nav-desktop__link sub-nav-desktop__link--child sub-nav-desktop__link--no-children" href="{% pageurl page %}" {% if last_menu_item and forloop.last %}data-last-menu-item-desktop{% endif %}>
88
{{ page.nav_text }}
99
</a>
1010
</li>

tbx/project_styleguide/templates/patterns/navigation/components/includes/subnav-menu-mini.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
<a
99
class="sub-nav-desktop-mini__link"
1010
href="{% pageurl link %}"
11+
{% if forloop.last %}
12+
data-last-menu-item-desktop
13+
{% endif %}
1114
>
1215
{{ link.nav_text }}
1316
</a>

tbx/project_styleguide/templates/patterns/navigation/components/includes/subnav-menu.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@
88
<a
99
class="sub-nav-desktop__link {% if children and child_display != 'hide_children' %}sub-nav-desktop__link--has-children{% else %}sub-nav-desktop__link--no-children{% endif %}"
1010
href="{% pageurl link %}"
11+
{% if forloop.last %}
12+
{% if not children or child_display == 'hide_children' %}data-last-menu-item-desktop{% endif %}{% endif %}
1113
>
1214
{{ link.nav_text }}
1315
</a>
14-
{% include "patterns/navigation/components/includes/subnav-child-menu.html" with pages=children %}
16+
{% if children and child_display != 'hide_children' %}
17+
{% include "patterns/navigation/components/includes/subnav-child-menu.html" with pages=children last_menu_item=forloop.last %}
18+
{% endif %}
1519
{% endwith %}
1620
</li>
1721
{% endfor %}

tbx/project_styleguide/templates/patterns/navigation/components/primary-nav-mobile.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
data-open-subnav aria-haspopup="true" aria-expanded="false"
1818
{% endif %}
1919
href="{% if link.page %}{% pageurl link.page %}{% elif link.external_link %}{{ link.external_link }}{% endif %}"
20+
{% if forloop.last %}data-last-menu-item-mobile{% endif %}
2021
>
2122
<span class="primary-nav-mobile__text">{{ link.text }}</span>
2223
{% if children and child_display != 'hide_children' %}

tbx/static_src/javascript/components/desktop-sub-menu.js

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,32 @@ class DesktopSubMenu {
1515
'[data-desktop-menu] [data-has-subnav]',
1616
);
1717
this.activeClass = 'active';
18+
this.lastMenuItems = document.querySelectorAll(
19+
'[data-last-menu-item-desktop]',
20+
);
1821
this.bindEventListeners();
1922
}
2023

24+
close() {
25+
this.toggleNode.classList.remove('active');
26+
this.node.setAttribute('aria-expanded', 'false');
27+
this.body.classList.remove('no-scroll');
28+
}
29+
30+
open() {
31+
// Fire a custom event which is useful if we need any other items such as
32+
// a search box to close when the desktop menu opens
33+
// Can be listened to with
34+
// document.addEventListener('onMenuOpen', () => {
35+
// // do stuff here...;
36+
// });
37+
const menuOpenEvent = new Event('onMenuOpen');
38+
document.dispatchEvent(menuOpenEvent);
39+
this.toggleNode.classList.add('active');
40+
this.node.setAttribute('aria-expanded', 'true');
41+
this.body.classList.add('no-scroll');
42+
}
43+
2144
bindEventListeners() {
2245
this.node.addEventListener('click', (e) => {
2346
e.preventDefault();
@@ -34,23 +57,18 @@ class DesktopSubMenu {
3457
});
3558

3659
if (this.toggleNode.classList.contains('active')) {
37-
this.toggleNode.classList.remove('active');
38-
this.node.setAttribute('aria-expanded', 'false');
39-
this.body.classList.remove('no-scroll');
60+
this.close();
4061
} else {
41-
// Fire a custom event which is useful if we need any other items such as
42-
// a search box to close when the desktop menu opens
43-
// Can be listened to with
44-
// document.addEventListener('onMenuOpen', () => {
45-
// // do stuff here...;
46-
// });
47-
const menuOpenEvent = new Event('onMenuOpen');
48-
document.dispatchEvent(menuOpenEvent);
49-
this.toggleNode.classList.add('active');
50-
this.node.setAttribute('aria-expanded', 'true');
51-
this.body.classList.add('no-scroll');
62+
this.open();
5263
}
5364
});
65+
66+
// Close the desktop menu when the focus moves away from the last item
67+
this.lastMenuItems.forEach((item) => {
68+
item.addEventListener('focusout', () => {
69+
this.close();
70+
});
71+
});
5472
}
5573
}
5674

tbx/static_src/javascript/components/mobile-menu.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ class MobileMenu {
77
this.node = node;
88
this.body = document.querySelector('body');
99
this.mobileMenu = document.querySelector('[data-mobile-menu]');
10+
this.lastMenuItem = document.querySelector(
11+
'[data-last-menu-item-mobile]',
12+
);
1013

1114
this.state = {
1215
open: false,
@@ -29,6 +32,18 @@ class MobileMenu {
2932
}
3033
}
3134
});
35+
36+
// Close the mobile menu when the focus moves away from the last item in the top level
37+
if (this.lastMenuItem === null) {
38+
return;
39+
}
40+
41+
this.lastMenuItem.addEventListener('focusout', () => {
42+
if (this.state.open) {
43+
this.close();
44+
this.state.open = false;
45+
}
46+
});
3247
}
3348

3449
toggle() {

0 commit comments

Comments
 (0)