Skip to content

Commit 4e6378e

Browse files
Unify keyboard events on docs.rs results
1 parent 905856b commit 4e6378e

File tree

3 files changed

+88
-64
lines changed

3 files changed

+88
-64
lines changed

static/keyboard.js

Lines changed: 85 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,115 @@
11
(function() {
2+
function focusSearchInput() {
3+
// On the index page, we have a "#search" input. If we are on this page, we want to go back
4+
// to this one and not the one in the header navbar.
5+
var searchInput = document.getElementById("search");
6+
if (searchInput) {
7+
searchInput.focus();
8+
} else {
9+
document.getElementById("nav-search").focus()
10+
}
11+
}
12+
13+
function focusFirstSearchResult() {
14+
var elem = document.querySelector(".recent-releases-container a.release");
15+
if (elem) {
16+
elem.focus();
17+
}
18+
}
19+
20+
function getWrappingLi(elem) {
21+
while (elem && elem.tagName !== "LI" && elem.tagName !== "BODY") {
22+
elem = elem.parentElement;
23+
}
24+
if (elem.tagName === "BODY") {
25+
return null;
26+
}
27+
return elem;
28+
}
29+
30+
function focusOnLi(li) {
31+
var elem = li.querySelector(".release");
32+
if (elem) {
33+
elem.focus();
34+
}
35+
}
36+
237
function getKey(ev) {
338
if ("key" in ev && typeof ev.key != "undefined") {
439
return ev.key;
540
}
641
return String.fromCharCode(ev.charCode || ev.keyCode);
742
}
843

9-
var active = null;
44+
function checkIfHasParent(elem, className) {
45+
while (elem && elem.tagName !== "BODY") {
46+
elem = elem.parentElement;
47+
if (elem.classList.contains(className)) {
48+
return true;
49+
}
50+
}
51+
return false;
52+
}
53+
1054
function handleKey(ev) {
11-
if (ev.ctrlKey || ev.altKey || ev.metaKey || document.activeElement.tagName === "INPUT") {
55+
if (ev.ctrlKey || ev.altKey || ev.metaKey) {
56+
return;
57+
}
58+
if (ev.which === 27) { // Escape
59+
document.activeElement.blur();
60+
return;
61+
}
62+
var tagName = document.activeElement.tagName;
63+
// We want to check two things here: if an input or an element of the docs.rs topbar
64+
// has the focus. If so, then we do nothing and simply return.
65+
if (tagName === "INPUT" ||
66+
(tagName === "A" &&
67+
checkIfHasParent(document.activeElement, "nav-container")))
68+
{
1269
return;
1370
}
1471

1572
if (ev.which === 40) { // Down arrow
1673
ev.preventDefault();
17-
if (active === null) {
18-
active = document.getElementsByClassName("recent-releases-container")[0].getElementsByTagName("li")[0];
19-
} else if (active.nextElementSibling) {
20-
active.classList.remove("selected");
21-
active = active.nextElementSibling;
74+
if (tagName === "BODY") {
75+
focusFirstSearchResult();
76+
} else {
77+
var wrappingLi = getWrappingLi(document.activeElement);
78+
if (!wrappingLi) {
79+
// Doesn't seem like we are in the crates list, let's focus the first element
80+
// of the list then!
81+
focusFirstSearchResult();
82+
} else if (wrappingLi.nextElementSibling) {
83+
focusOnLi(wrappingLi.nextElementSibling);
84+
}
2285
}
23-
active.classList.add("selected");
2486
} else if (ev.which === 38) { // Up arrow
2587
ev.preventDefault();
26-
if (active === null) {
27-
active = document.getElementsByClassName("recent-releases-container")[0].getElementsByTagName("li")[0];
28-
} else if (active.previousElementSibling) {
29-
active.classList.remove("selected");
30-
active = active.previousElementSibling;
31-
}
32-
active.classList.add("selected");
33-
active.focus();
34-
} else if (ev.which === 13) { // Return
35-
if (active !== null) {
36-
document.location.href = active.getElementsByTagName("a")[0].href;
88+
if (tagName === "A") {
89+
var wrappingLi = getWrappingLi(document.activeElement);
90+
if (!wrappingLi) {
91+
// Doesn't seem like we are in the crates list, let's focus the first element
92+
// of the list then!
93+
focusFirstSearchResult();
94+
} else if (wrappingLi.previousElementSibling) {
95+
focusOnLi(wrappingLi.previousElementSibling);
96+
} else {
97+
focusSearchInput();
98+
}
99+
} else if (tagName === "BODY") {
100+
focusFirstSearchResult();
37101
}
38-
} else {
102+
} else if (tagName !== "INPUT") {
39103
switch (getKey(ev)) {
40104
case "s":
41105
case "S":
42106
ev.preventDefault();
43-
var searchInputNav = document.getElementsByClassName("search-input-nav");
44-
if (searchInputNav.length > 0) {
45-
searchInputNav[0].focus();
46-
}
107+
focusSearchInput();
47108
break;
48109
}
49110
}
50111
}
51112

52113
document.onkeypress = handleKey;
53114
document.onkeydown = handleKey;
54-
55-
var crates = Array.prototype.slice.call(document.getElementsByClassName("recent-releases-container")[0].getElementsByTagName("li"));
56-
for (var i = 0; i < crates.length; ++i) {
57-
crates[i].addEventListener("mouseover", function (event) {
58-
this.classList.remove("selected");
59-
active = null;
60-
});
61-
crates[i].addEventListener("mouseout", function (event) {
62-
this.classList.remove("selected");
63-
active = null;
64-
});
65-
}
66115
})();

templates/core/home.html

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -72,30 +72,5 @@ <h1 class="brand">{{ "cubes" | fas(fw=true) }} Docs.rs</h1>
7272
{%- endblock body -%}
7373

7474
{%- block javascript -%}
75-
<script type="text/javascript" nonce="{{ csp_nonce }}">
76-
function getKey(ev) {
77-
if ("key" in ev && typeof ev.key != "undefined") {
78-
return ev.key;
79-
}
80-
81-
return String.fromCharCode(ev.charCode || ev.keyCode);
82-
}
83-
84-
function handleShortcut(ev) {
85-
if (ev.ctrlKey || ev.altKey || ev.metaKey || document.activeElement.tagName === "INPUT") {
86-
return;
87-
}
88-
89-
switch (getKey(ev)) {
90-
case "s":
91-
case "S":
92-
ev.preventDefault();
93-
document.getElementById("search").focus();
94-
break;
95-
}
96-
}
97-
98-
document.onkeypress = handleShortcut;
99-
document.onkeydown = handleShortcut;
100-
</script>
75+
<script nonce="{{ csp_nonce }}" type="text/javascript" src="/-/static/keyboard.js?{{ docsrs_version() | slugify }}"></script>
10176
{%- endblock javascript -%}

templates/style/base.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,11 @@ div.recent-releases-container {
293293
}
294294

295295
.release:hover,
296-
li.selected > .release {
296+
a.release:focus {
297297
background-color: var(--color-background-code);
298298
}
299299

300-
li:last-child .release {
300+
li:last-of-type .release {
301301
border-bottom: none;
302302
}
303303

0 commit comments

Comments
 (0)