Search: Keyboard Bindings for easier navigation (#138)
* set up basic actions on keypress * add actions to perform conditionally * add simple highlight animation * prevent mouseclick to change active element * clear input box on Escape * click on Arrow Right * clear results and focus search-input on esc * refactor
This commit is contained in:
parent
d6b2282582
commit
b7f8749cdf
|
@ -1,4 +1,4 @@
|
||||||
.searchbox input {
|
#searchbox input {
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
|
@ -7,7 +7,7 @@
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchbox input:focus {
|
#searchbox input:focus {
|
||||||
border-color: var(--secondary);
|
border-color: var(--secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,3 +38,7 @@
|
||||||
left: 0px;
|
left: 0px;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#searchResults .active {
|
||||||
|
transform: scale(.98);
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
var fuse; // holds our search engine
|
var fuse; // holds our search engine
|
||||||
|
var resList = document.getElementById('searchResults');
|
||||||
|
var sInput = document.getElementById('searchInput');
|
||||||
|
var first, last = null
|
||||||
|
var resultsAvailable = false;
|
||||||
|
|
||||||
// load our search index, only executed onload
|
// load our search index, only executed onload
|
||||||
function loadSearch() {
|
function loadSearch() {
|
||||||
|
@ -35,6 +39,15 @@ function loadSearch() {
|
||||||
xhr.send();
|
xhr.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function itemGen(name, link) {
|
||||||
|
return `<li class="post-entry"><header class="entry-header">${name} »</header><a href="${link}" aria-label="${name}"></a></li>`
|
||||||
|
}
|
||||||
|
|
||||||
|
function activeToggle() {
|
||||||
|
document.activeElement.parentElement.classList.toggle("active")
|
||||||
|
}
|
||||||
|
|
||||||
// execute search as each character is typed
|
// execute search as each character is typed
|
||||||
document.getElementById("searchInput").onkeyup = function (e) {
|
document.getElementById("searchInput").onkeyup = function (e) {
|
||||||
// run a search query (for "term") every time a letter is typed
|
// run a search query (for "term") every time a letter is typed
|
||||||
|
@ -50,11 +63,60 @@ document.getElementById("searchInput").onkeyup = function (e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById("searchResults").innerHTML = resultSet;
|
document.getElementById("searchResults").innerHTML = resultSet;
|
||||||
|
resultsAvailable = true;
|
||||||
|
first = resList.firstChild;
|
||||||
|
last = resList.lastChild;
|
||||||
} else {
|
} else {
|
||||||
|
resultsAvailable = false;
|
||||||
document.getElementById("searchResults").innerHTML = '';
|
document.getElementById("searchResults").innerHTML = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function itemGen(name, link) {
|
// kb bindings
|
||||||
return `<li class="post-entry"><header class="entry-header">${name} »</header><a href="${link}" aria-label="${name}"></a></li>`
|
document.onkeydown = function (e) {
|
||||||
|
let key = e.key;
|
||||||
|
let ae = document.activeElement;
|
||||||
|
|
||||||
|
if (key === "ArrowDown" && resultsAvailable) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (ae == sInput) {
|
||||||
|
// if the currently focused element is the search input, focus the <a> of first <li>
|
||||||
|
activeToggle(); // rm active class
|
||||||
|
resList.firstChild.lastChild.focus();
|
||||||
|
activeToggle(); // add active class
|
||||||
|
} else if (ae.parentElement == last) {
|
||||||
|
// if the currently focused element's parent is last, do nothing
|
||||||
|
} else {
|
||||||
|
// otherwise select the next search result
|
||||||
|
activeToggle(); // rm active class
|
||||||
|
ae.parentElement.nextSibling.lastChild.focus();
|
||||||
|
activeToggle(); // add active class
|
||||||
|
}
|
||||||
|
} else if (key === "ArrowUp" && resultsAvailable) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (ae == sInput) {
|
||||||
|
// if the currently focused element is input box, do nothing
|
||||||
|
} else if (ae.parentElement == first) {
|
||||||
|
// if the currently focused element is first item, go to input box
|
||||||
|
activeToggle(); // rm active class
|
||||||
|
sInput.focus();
|
||||||
|
} else {
|
||||||
|
// otherwise select the previous search result
|
||||||
|
activeToggle(); // rm active class
|
||||||
|
ae.parentElement.previousSibling.lastChild.focus();
|
||||||
|
activeToggle(); // add active class
|
||||||
|
}
|
||||||
|
} else if (key === "ArrowRight" && resultsAvailable) {
|
||||||
|
ae.click(); // click on active link
|
||||||
|
} else if (key === "Escape") {
|
||||||
|
resultsAvailable = false;
|
||||||
|
document.getElementById("searchResults").innerHTML = sInput.value = ''; // clear inputbox and searchResults
|
||||||
|
sInput.focus(); // shift focus to input box
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.onmousedown = function (e) {
|
||||||
|
if (e.type === "mousedown") {
|
||||||
|
e.preventDefault(); // prevent mousedown to change focus
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
{{- end}}
|
{{- end}}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="searchbox">
|
<div id="searchbox">
|
||||||
<input id="searchInput" autofocus placeholder="{{.Title}} ↵" aria-label="search">
|
<input id="searchInput" autofocus placeholder="{{.Title}} ↵" aria-label="search">
|
||||||
<ul id="searchResults" aria-label="search results"></ul>
|
<ul id="searchResults" aria-label="search results"></ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue