127 lines
4.4 KiB
JavaScript
127 lines
4.4 KiB
JavaScript
|
let searchInput;
|
||
|
let currentFocus;
|
||
|
let originalSearch;
|
||
|
let autocompleteResults;
|
||
|
|
||
|
const handleUserInput = () => {
|
||
|
let xhrRequest = new XMLHttpRequest();
|
||
|
xhrRequest.open("POST", "autocomplete");
|
||
|
xhrRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||
|
xhrRequest.onload = function () {
|
||
|
if (xhrRequest.readyState === 4 && xhrRequest.status !== 200) {
|
||
|
// Do nothing if failed to fetch autocomplete results
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Fill autocomplete with fetched results
|
||
|
autocompleteResults = JSON.parse(xhrRequest.responseText)[1];
|
||
|
updateAutocompleteList();
|
||
|
};
|
||
|
|
||
|
xhrRequest.send('q=' + searchInput.value);
|
||
|
};
|
||
|
|
||
|
const removeActive = suggestion => {
|
||
|
// Remove "autocomplete-active" class from previously active suggestion
|
||
|
for (let i = 0; i < suggestion.length; i++) {
|
||
|
suggestion[i].classList.remove("autocomplete-active");
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const addActive = (suggestion) => {
|
||
|
// Handle navigation outside of suggestion list
|
||
|
if (!suggestion || !suggestion[currentFocus]) {
|
||
|
if (currentFocus >= suggestion.length) {
|
||
|
// Move selection back to the beginning
|
||
|
currentFocus = 0;
|
||
|
} else if (currentFocus < 0) {
|
||
|
// Retrieve original search and remove active suggestion selection
|
||
|
currentFocus = -1;
|
||
|
searchInput.value = originalSearch;
|
||
|
removeActive(suggestion);
|
||
|
return;
|
||
|
} else {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
removeActive(suggestion);
|
||
|
suggestion[currentFocus].classList.add("autocomplete-active");
|
||
|
|
||
|
// Autofill search bar with suggestion content (minus the "bang name" if using a bang operator)
|
||
|
let searchContent = suggestion[currentFocus].textContent;
|
||
|
if (searchContent.indexOf('(') > 0) {
|
||
|
searchInput.value = searchContent.substring(0, searchContent.indexOf('('));
|
||
|
} else {
|
||
|
searchInput.value = searchContent;
|
||
|
}
|
||
|
|
||
|
searchInput.focus();
|
||
|
};
|
||
|
|
||
|
const autocompleteInput = (e) => {
|
||
|
// Handle navigation between autocomplete suggestions
|
||
|
let suggestion = document.getElementById("autocomplete-list");
|
||
|
if (suggestion) suggestion = suggestion.getElementsByTagName("div");
|
||
|
if (e.keyCode === 40) { // down
|
||
|
e.preventDefault();
|
||
|
currentFocus++;
|
||
|
addActive(suggestion);
|
||
|
} else if (e.keyCode === 38) { //up
|
||
|
e.preventDefault();
|
||
|
currentFocus--;
|
||
|
addActive(suggestion);
|
||
|
} else if (e.keyCode === 13) { // enter
|
||
|
e.preventDefault();
|
||
|
if (currentFocus > -1) {
|
||
|
if (suggestion) suggestion[currentFocus].click();
|
||
|
}
|
||
|
} else {
|
||
|
originalSearch = searchInput.value;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const updateAutocompleteList = () => {
|
||
|
let autocompleteItem, i;
|
||
|
let val = originalSearch;
|
||
|
|
||
|
let autocompleteList = document.getElementById("autocomplete-list");
|
||
|
autocompleteList.innerHTML = "";
|
||
|
|
||
|
if (!val || !autocompleteResults) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
currentFocus = -1;
|
||
|
|
||
|
for (i = 0; i < autocompleteResults.length; i++) {
|
||
|
if (autocompleteResults[i].substr(0, val.length).toUpperCase() === val.toUpperCase()) {
|
||
|
autocompleteItem = document.createElement("div");
|
||
|
autocompleteItem.setAttribute("class", "autocomplete-item");
|
||
|
autocompleteItem.innerHTML = "<strong>" + autocompleteResults[i].substr(0, val.length) + "</strong>";
|
||
|
autocompleteItem.innerHTML += autocompleteResults[i].substr(val.length);
|
||
|
autocompleteItem.innerHTML += "<input type=\"hidden\" value=\"" + autocompleteResults[i] + "\">";
|
||
|
autocompleteItem.addEventListener("click", function () {
|
||
|
searchInput.value = this.getElementsByTagName("input")[0].value;
|
||
|
autocompleteList.innerHTML = "";
|
||
|
document.getElementById("search-form").submit();
|
||
|
});
|
||
|
autocompleteList.appendChild(autocompleteItem);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
document.addEventListener("DOMContentLoaded", function() {
|
||
|
let autocompleteList = document.createElement("div");
|
||
|
autocompleteList.setAttribute("id", "autocomplete-list");
|
||
|
autocompleteList.setAttribute("class", "autocomplete-items");
|
||
|
|
||
|
searchInput = document.getElementById("search-bar");
|
||
|
searchInput.parentNode.appendChild(autocompleteList);
|
||
|
|
||
|
searchInput.addEventListener("keydown", (event) => autocompleteInput(event));
|
||
|
|
||
|
document.addEventListener("click", function (e) {
|
||
|
autocompleteList.innerHTML = "";
|
||
|
});
|
||
|
});
|