diff --git a/app/static/js/autocomplete.js b/app/static/js/autocomplete.js index 702ebc4..87d0c42 100644 --- a/app/static/js/autocomplete.js +++ b/app/static/js/autocomplete.js @@ -1,4 +1,9 @@ -const handleUserInput = searchBar => { +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"); @@ -9,118 +14,119 @@ const handleUserInput = searchBar => { } // Fill autocomplete with fetched results - let autocompleteResults = JSON.parse(xhrRequest.responseText); - autocomplete(searchBar, autocompleteResults[1]); + autocompleteResults = JSON.parse(xhrRequest.responseText)[1]; + updateAutocompleteList(); }; - xhrRequest.send('q=' + searchBar.value); + xhrRequest.send('q=' + searchInput.value); }; -const autocomplete = (searchInput, autocompleteResults) => { - let currentFocus; - let originalSearch; - - searchInput.addEventListener("input", function () { - let autocompleteList, autocompleteItem, i, val = this.value; - closeAllLists(); - - if (!val || !autocompleteResults) { - return false; +const closeAllLists = el => { + // Close all autocomplete suggestions + let suggestions = document.getElementsByClassName("autocomplete-items"); + for (let i = 0; i < suggestions.length; i++) { + if (el !== suggestions[i] && el !== searchInput) { + suggestions[i].parentNode.removeChild(suggestions[i]); } + } +}; - currentFocus = -1; - autocompleteList = document.createElement("div"); - autocompleteList.setAttribute("id", this.id + "-autocomplete-list"); - autocompleteList.setAttribute("class", "autocomplete-items"); - this.parentNode.appendChild(autocompleteList); +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"); + } +}; - for (i = 0; i < autocompleteResults.length; i++) { - if (autocompleteResults[i].substr(0, val.length).toUpperCase() === val.toUpperCase()) { - autocompleteItem = document.createElement("div"); - autocompleteItem.innerHTML = "" + autocompleteResults[i].substr(0, val.length) + ""; - autocompleteItem.innerHTML += autocompleteResults[i].substr(val.length); - autocompleteItem.innerHTML += ""; - autocompleteItem.addEventListener("click", function () { - searchInput.value = this.getElementsByTagName("input")[0].value; - closeAllLists(); - document.getElementById("search-form").submit(); - }); - autocompleteList.appendChild(autocompleteItem); - } - } - }); - - searchInput.addEventListener("keydown", function (e) { - let suggestion = document.getElementById(this.id + "-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(); - } +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 { - originalSearch = document.getElementById("search-bar").value; + return; } - }); + } - const addActive = suggestion => { - let searchBar = document.getElementById("search-bar"); + removeActive(suggestion); + suggestion[currentFocus].classList.add("autocomplete-active"); - // 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; - searchBar.value = originalSearch; - removeActive(suggestion); - return; - } else { - return; - } + // 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(this.id + "-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; + } +}; - removeActive(suggestion); - suggestion[currentFocus].classList.add("autocomplete-active"); +const updateAutocompleteList = () => { + let autocompleteList, autocompleteItem, i; + let val = originalSearch; + closeAllLists(); - // Autofill search bar with suggestion content (minus the "bang name" if using a bang operator) - let searchContent = suggestion[currentFocus].textContent; - if (searchContent.indexOf('(') > 0) { - searchBar.value = searchContent.substring(0, searchContent.indexOf('(')); - } else { - searchBar.value = searchContent; + if (!val || !autocompleteResults) { + return false; + } + + currentFocus = -1; + autocompleteList = document.createElement("div"); + autocompleteList.setAttribute("id", this.id + "-autocomplete-list"); + autocompleteList.setAttribute("class", "autocomplete-items"); + searchInput.parentNode.appendChild(autocompleteList); + + for (i = 0; i < autocompleteResults.length; i++) { + if (autocompleteResults[i].substr(0, val.length).toUpperCase() === val.toUpperCase()) { + autocompleteItem = document.createElement("div"); + autocompleteItem.innerHTML = "" + autocompleteResults[i].substr(0, val.length) + ""; + autocompleteItem.innerHTML += autocompleteResults[i].substr(val.length); + autocompleteItem.innerHTML += ""; + autocompleteItem.addEventListener("click", function () { + searchInput.value = this.getElementsByTagName("input")[0].value; + closeAllLists(); + document.getElementById("search-form").submit(); + }); + autocompleteList.appendChild(autocompleteItem); } + } +}; - searchBar.focus(); - }; +document.addEventListener("DOMContentLoaded", function() { + searchInput = document.getElementById("search-bar"); + searchInput.addEventListener("keydown", (event) => autocompleteInput(event)); - const removeActive = suggestion => { - for (let i = 0; i < suggestion.length; i++) { - suggestion[i].classList.remove("autocomplete-active"); - } - }; - - const closeAllLists = el => { - let suggestions = document.getElementsByClassName("autocomplete-items"); - for (let i = 0; i < suggestions.length; i++) { - if (el !== suggestions[i] && el !== searchInput) { - suggestions[i].parentNode.removeChild(suggestions[i]); - } - } - }; - - // Close lists and search when user selects a suggestion document.addEventListener("click", function (e) { closeAllLists(e.target); }); -}; +}); \ No newline at end of file diff --git a/app/static/js/controller.js b/app/static/js/controller.js index 05b68ec..9f05e16 100644 --- a/app/static/js/controller.js +++ b/app/static/js/controller.js @@ -2,6 +2,8 @@ const setupSearchLayout = () => { // Setup search field const searchBar = document.getElementById("search-bar"); const searchBtn = document.getElementById("search-submit"); + const arrowKeys = [37, 38, 39, 40]; + let searchValue = searchBar.value; // Automatically focus on search field searchBar.focus(); @@ -11,8 +13,9 @@ const setupSearchLayout = () => { if (event.keyCode === 13) { event.preventDefault(); searchBtn.click(); - } else { - handleUserInput(searchBar); + } else if (searchBar.value !== searchValue && !arrowKeys.includes(event.keyCode)) { + searchValue = searchBar.value; + handleUserInput(); } }); };