diff --git a/static/keyboard.js b/static/keyboard.js index d18c5cb49..24151e7ec 100644 --- a/static/keyboard.js +++ b/static/keyboard.js @@ -1,4 +1,39 @@ (function() { + function focusSearchInput() { + // On the index page, we have a "#search" input. If we are on this page, we want to go back + // to this one and not the one in the header navbar. + var searchInput = document.getElementById("search"); + if (searchInput) { + searchInput.focus(); + } else { + document.getElementById("nav-search").focus() + } + } + + function focusFirstSearchResult() { + var elem = document.querySelector(".recent-releases-container a.release"); + if (elem) { + elem.focus(); + } + } + + function getWrappingLi(elem) { + while (elem && elem.tagName !== "LI" && elem.tagName !== "BODY") { + elem = elem.parentElement; + } + if (elem.tagName === "BODY") { + return null; + } + return elem; + } + + function focusOnLi(li) { + var elem = li.querySelector(".release"); + if (elem) { + elem.focus(); + } + } + function getKey(ev) { if ("key" in ev && typeof ev.key != "undefined") { return ev.key; @@ -6,44 +41,70 @@ return String.fromCharCode(ev.charCode || ev.keyCode); } - var active = null; + function checkIfHasParent(elem, className) { + while (elem && elem.tagName !== "BODY") { + elem = elem.parentElement; + if (elem.classList.contains(className)) { + return true; + } + } + return false; + } + function handleKey(ev) { - if (ev.ctrlKey || ev.altKey || ev.metaKey || document.activeElement.tagName === "INPUT") { + if (ev.ctrlKey || ev.altKey || ev.metaKey) { + return; + } + if (ev.which === 27) { // Escape + document.activeElement.blur(); + return; + } + var tagName = document.activeElement.tagName; + // We want to check two things here: if an input or an element of the docs.rs topbar + // has the focus. If so, then we do nothing and simply return. + if (tagName === "INPUT" || + (tagName === "A" && + checkIfHasParent(document.activeElement, "nav-container"))) + { return; } if (ev.which === 40) { // Down arrow ev.preventDefault(); - if (active === null) { - active = document.getElementsByClassName("recent-releases-container")[0].getElementsByTagName("li")[0]; - } else if (active.nextElementSibling) { - active.classList.remove("selected"); - active = active.nextElementSibling; + if (tagName === "BODY") { + focusFirstSearchResult(); + } else { + var wrappingLi = getWrappingLi(document.activeElement); + if (!wrappingLi) { + // Doesn't seem like we are in the crates list, let's focus the first element + // of the list then! + focusFirstSearchResult(); + } else if (wrappingLi.nextElementSibling) { + focusOnLi(wrappingLi.nextElementSibling); + } } - active.classList.add("selected"); } else if (ev.which === 38) { // Up arrow ev.preventDefault(); - if (active === null) { - active = document.getElementsByClassName("recent-releases-container")[0].getElementsByTagName("li")[0]; - } else if (active.previousElementSibling) { - active.classList.remove("selected"); - active = active.previousElementSibling; - } - active.classList.add("selected"); - active.focus(); - } else if (ev.which === 13) { // Return - if (active !== null) { - document.location.href = active.getElementsByTagName("a")[0].href; + if (tagName === "A") { + var wrappingLi = getWrappingLi(document.activeElement); + if (!wrappingLi) { + // Doesn't seem like we are in the crates list, let's focus the first element + // of the list then! + focusFirstSearchResult(); + } else if (wrappingLi.previousElementSibling) { + focusOnLi(wrappingLi.previousElementSibling); + } else { + focusSearchInput(); + } + } else if (tagName === "BODY") { + focusFirstSearchResult(); } - } else { + } else if (tagName !== "INPUT") { switch (getKey(ev)) { case "s": case "S": ev.preventDefault(); - var searchInputNav = document.getElementsByClassName("search-input-nav"); - if (searchInputNav.length > 0) { - searchInputNav[0].focus(); - } + focusSearchInput(); break; } } @@ -51,16 +112,4 @@ document.onkeypress = handleKey; document.onkeydown = handleKey; - - var crates = Array.prototype.slice.call(document.getElementsByClassName("recent-releases-container")[0].getElementsByTagName("li")); - for (var i = 0; i < crates.length; ++i) { - crates[i].addEventListener("mouseover", function (event) { - this.classList.remove("selected"); - active = null; - }); - crates[i].addEventListener("mouseout", function (event) { - this.classList.remove("selected"); - active = null; - }); - } })(); diff --git a/templates/core/home.html b/templates/core/home.html index b10cb3f6c..f7801cb5b 100644 --- a/templates/core/home.html +++ b/templates/core/home.html @@ -72,30 +72,5 @@

{{ "cubes" | fas(fw=true) }} Docs.rs

{%- endblock body -%} {%- block javascript -%} - + {%- endblock javascript -%} diff --git a/templates/style/base.scss b/templates/style/base.scss index 21cae4945..d3d7731b6 100644 --- a/templates/style/base.scss +++ b/templates/style/base.scss @@ -293,11 +293,11 @@ div.recent-releases-container { } .release:hover, - li.selected > .release { + a.release:focus { background-color: var(--color-background-code); } - li:last-child .release { + li:last-of-type .release { border-bottom: none; }