Skip to content

Commit db5c875

Browse files
committed
Make pure menu dropdowns keyboard accessible
Fixes rust-lang#67
1 parent 10dc6f5 commit db5c875

File tree

1 file changed

+171
-0
lines changed

1 file changed

+171
-0
lines changed

templates/footer.hbs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,174 @@
11
{{#if varsb.javascript_highlightjs}}<script type="text/javascript" charset="utf-8">hljs.initHighlighting();</script>{{/if}}
2+
<script type="text/javascript">
3+
// Allow menus to be open and used by keyboard.
4+
(function() {
5+
var currentMenu;
6+
var backdrop = document.createElement("div");
7+
backdrop.style = "display:none;position:fixed;width:100%;height:100%;z-index:1;background:rgba(50, 50, 50, 0.5)";
8+
document.documentElement.insertBefore(backdrop, document.querySelector("body"));
9+
function previous(allItems, item) {
10+
var i = 1;
11+
var l = allItems.length;
12+
while (i < l) {
13+
if (allItems[i] == item) {
14+
return allItems[i - 1];
15+
}
16+
i += 1;
17+
}
18+
}
19+
function next(allItems, item) {
20+
var i = 0;
21+
var l = allItems.length - 1;
22+
while (i < l) {
23+
if (allItems[i] == item) {
24+
return allItems[i + 1];
25+
}
26+
i += 1;
27+
}
28+
}
29+
function last(allItems) {
30+
return allItems[allItems.length - 1];
31+
}
32+
function closeMenu() {
33+
currentMenu.className = currentMenu.className.replace("pure-menu-active", "");
34+
currentMenu = null;
35+
backdrop.style.display = "none";
36+
}
37+
backdrop.onclick = closeMenu;
38+
function openMenu(newMenu) {
39+
currentMenu = newMenu;
40+
newMenu.className += " pure-menu-active";
41+
backdrop.style.display = "block";
42+
}
43+
function menuOnClick(e) {
44+
if (this.parentNode === currentMenu) {
45+
closeMenu();
46+
} else {
47+
if (currentMenu) closeMenu();
48+
openMenu(this.parentNode);
49+
}
50+
e.preventDefault();
51+
e.stopPropagation();
52+
};
53+
function menuMouseOver(e) {
54+
if (currentMenu) {
55+
if (e.target.className.indexOf("pure-menu-link") !== -1) {
56+
e.target.focus();
57+
}
58+
}
59+
}
60+
function menuKeyDown(e) {
61+
if (currentMenu) {
62+
var children = currentMenu.querySelector(".pure-menu-children");
63+
var currentLink = children.querySelector(".pure-menu-link:focus");
64+
var currentItem;
65+
if (currentLink && currentLink.parentNode.className.indexOf("pure-menu-item") !== -1) {
66+
currentItem = currentLink.parentNode;
67+
}
68+
var allItems = [];
69+
if (children) {
70+
allItems = children.querySelectorAll(".pure-menu-item .pure-menu-link");
71+
}
72+
var switchTo = null;
73+
switch (e.key.toLowerCase()) {
74+
case "escape":
75+
case "esc":
76+
closeMenu();
77+
e.preventDefault();
78+
e.stopPropagation();
79+
return;
80+
case "arrowdown":
81+
case "down":
82+
if (currentLink) {
83+
// Arrow down when an item other than the last is focused: focus next item.
84+
// Arrow down when the last item is focused: jump to top.
85+
switchTo = (next(allItems, currentLink) || allItems[0]);
86+
} else {
87+
// Arrow down when a menu is open and nothing is focused: focus first item.
88+
switchTo = allItems[0];
89+
}
90+
break;
91+
case "arrowup":
92+
case "up":
93+
if (currentLink) {
94+
// Arrow up when an item other than the first is focused: focus previous item.
95+
// Arrow up when the first item is focused: jump to bottom.
96+
switchTo = (previous(allItems, currentLink) || last(allItems));
97+
} else {
98+
// Arrow up when a menu is open and nothing is focused: focus last item.
99+
switchTo = last(allItems);
100+
}
101+
break;
102+
case "tab":
103+
if (!currentLink) {
104+
// if the menu is open, we should focus trap into it
105+
// this is the behavior of the WAI example
106+
// it is not the same as GitHub, but GitHub allows you to tab yourself out
107+
// of the menu without closing it (which is horrible behavior)
108+
switchTo = e.shiftKey ? last(allItems) : allItems[0];
109+
} else if (e.shiftKey && currentItem === allItems[0]) {
110+
// if you tab your way out of the menu, close it
111+
// this is neither what GitHub nor the WAI example do,
112+
// but is a rationalization of GitHub's behavior: we don't want users who know how to
113+
// use tab and enter, but don't know that they can close menus with Escape,
114+
// to find themselves completely trapped in the menu
115+
currentMenu.firstElementChild.focus();
116+
closeMenu();
117+
e.preventDefault();
118+
e.stopPropagation();
119+
} else if (!e.shiftKey && currentLink === last(allItems)) {
120+
// same as above.
121+
// if you tab your way out of the menu, close it
122+
closeMenu();
123+
}
124+
break;
125+
case "enter":
126+
case "return":
127+
case "space":
128+
// enter, return, and space have the default browser behavior,
129+
// but they also close the menu
130+
// this behavior is identical between both the WAI example, and GitHub's
131+
setTimeout(function() {
132+
closeMenu();
133+
}, 100);
134+
break;
135+
case "home":
136+
// home: focus first menu item.
137+
// This is the behavior of WAI, while GitHub scrolls,
138+
// but it's unlikely that a user will try to scroll the page while the menu is open,
139+
// so they won't do it on accident.
140+
switchTo = allItems[0];
141+
break;
142+
case "end":
143+
// end: focus last menu item.
144+
// This is the behavior of WAI, while GitHub scrolls,
145+
// but it's unlikely that a user will try to scroll the page while the menu is open,
146+
// so they won't do it on accident.
147+
switchTo = last(allItems);
148+
break;
149+
}
150+
if (switchTo) {
151+
var switchToLink = switchTo.querySelector("a");
152+
if (switchToLink) {
153+
switchToLink.focus();
154+
} else {
155+
switchTo.focus();
156+
}
157+
e.preventDefault();
158+
e.stopPropagation();
159+
}
160+
}
161+
};
162+
var menus = Array.prototype.slice.call(document.querySelectorAll(".pure-menu-has-children"));
163+
var menusLength = menus.length;
164+
var menu;
165+
for (var i = 0; i < menusLength; ++i) {
166+
menu = menus[i];
167+
menu.firstElementChild.addEventListener("click", menuOnClick);
168+
menu.addEventListener("mouseover", menuMouseOver);
169+
}
170+
document.documentElement.addEventListener("keydown", menuKeyDown);
171+
})();
172+
</script>
2173
</body>
3174
</html>

0 commit comments

Comments
 (0)