Reputation: 11
I am trying to create an autocomplete search with suggestions from the OMDB API. I used the code written by w3schools' example except tweaked it a little to use an array returned from a separate function but none of the data shows up on the suggestion list. When I put the return statement outside of the $.getJSON function and declare the array with some test values, then the suggestions show up but only with the test values and not any new values that were previously pushed from the OMDB API, most likely because of the asynchronous nature of the function. Putting the return statement in the callback function results in no suggestions at all. I don't understand why the returned array isn't being read. Here is the file:
extends layout
block layout - content
head
meta(charset = "utf-8")
meta(name = "viewport"
content = "width=device-width, initial-scale=1")
link(rel = "stylesheet"
href = "https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css")
script(src = "https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js")
script(src = "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js")
script(src = "https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js")
link(rel = "stylesheet", href = "https://www.w3schools.com/w3css/4/w3.css")
link(rel = "stylesheet", href = "https://fonts.googleapis.com/css?family=Raleway")
body.bgimg
div(class = "row"
id = "row")
div(class = "col-sm-2")
div(class = "col-sm-8"
id = "test"
style = "background-color:white;")
div(class = 'w3-container')
h4 Safwat
form(autocomplete = "off"
action = "/action_page.php")
div(class = "autocomplete")
input(id = "myInput"
type = "text"
name = "myCountry"
placeholder = "Select a movie or tv show")
input(type = "submit")
input(type = "submit")
script(src = "https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js")
script.
function getSearchResults(search) {
var titlesForFunc = [];
$.getJSON("https://www.omdbapi.com/?", {
apikey: "12687fb7",
s: search
}, function (data) {
$.each(data.Search, function (index, value) {
if (index < 4) {
titlesForFunc.push(value.Title);
}
})
return titlesForFunc;
})
}
function autocomplete(inp) {
/*the autocomplete function takes two arguments,
the text field element and an array of possible autocompleted values:*/
var currentFocus;
/*execute a function when someone writes in the text field:*/
inp.addEventListener("input", function (e) {
var a, b, i, val = this.value;
/*close any already open lists of autocompleted values*/
closeAllLists();
if (!val) {
return false;
}
currentFocus = -1;
/*create a DIV element that will contain the items (values):*/
a = document.createElement("DIV");
a.setAttribute("id", this.id + "autocomplete-list");
a.setAttribute("class", "autocomplete-items");
/*append the DIV element as a child of the autocomplete container:*/
this.parentNode.appendChild(a);
var arr = getSearchResults(val);
/*for each item in the array...*/
for (i = 0; i < arr.length; i++) {
/*check if the item starts with the same letters as the text field value:*/
/*create a DIV element for each matching element:*/
b = document.createElement("DIV");
b.innerHTML = arr[i];
/*insert a input field that will hold the current array item's value:*/
b.innerHTML += "<input type='hidden' value='" + arr[i] + "'>";
/*execute a function when someone clicks on the item value (DIV element):*/
b.addEventListener("click", function (e) {
/*insert the value for the autocomplete text field:*/
inp.value = this.getElementsByTagName("input")[0].value;
/*close the list of autocompleted values,
(or any other open lists of autocompleted values:*/
closeAllLists();
});
a.appendChild(b);
}
});
/*execute a function presses a key on the keyboard:*/
inp.addEventListener("keydown", function (e) {
var x = document.getElementById(this.id + "autocomplete-list");
if (x) x = x.getElementsByTagName("div");
if (e.keyCode == 40) {
/*If the arrow DOWN key is pressed,
increase the currentFocus variable:*/
currentFocus++;
/*and and make the current item more visible:*/
addActive(x);
} else if (e.keyCode == 38) { //up
/*If the arrow UP key is pressed,
decrease the currentFocus variable:*/
currentFocus--;
/*and and make the current item more visible:*/
addActive(x);
} else if (e.keyCode == 13) {
/*If the ENTER key is pressed, prevent the form from being submitted,*/
e.preventDefault();
if (currentFocus > -1) {
/*and simulate a click on the "active" item:*/
if (x) x[currentFocus].click();
}
}
});
function addActive(x) {
/*a function to classify an item as "active":*/
if (!x) return false;
/*start by removing the "active" class on all items:*/
removeActive(x);
if (currentFocus >= x.length) currentFocus = 0;
if (currentFocus < 0) currentFocus = (x.length - 1);
/*add class "autocomplete-active":*/
x[currentFocus].classList.add("autocomplete-active");
}
function removeActive(x) {
/*a function to remove the "active" class from all autocomplete items:*/
for (var i = 0; i < x.length; i++) {
x[i].classList.remove("autocomplete-active");
}
}
function closeAllLists(elmnt) {
/*close all autocomplete lists in the document,
except the one passed as an argument:*/
var x = document.getElementsByClassName("autocomplete-items");
for (var i = 0; i < x.length; i++) {
if (elmnt != x[i] && elmnt != inp) {
x[i].parentNode.removeChild(x[i]);
}
}
}
/*execute a function when someone clicks in the document:*/
document.addEventListener("click", function (e) {
closeAllLists(e.target);
});
}
autocomplete(document.getElementById("myInput"));
Upvotes: 1
Views: 126
Reputation: 399
Your version of getSearchResults
implicitly returns undefined
. Also, putting a return statement into the callback function of $.getJSON
doesn't do what you expect it to - it cannot return the array to the caller because it's internal to getJSON
.
To solve the issue, you would have to:
getSearchResults
asynchronous, for example using a Promisefunction getSearchResults(search) {
var titlesForFunc = [];
return new Promise(function(resolve, reject) {
$.getJSON("https://www.omdbapi.com/?", {
apikey: "12687fb7",
s: search
}, function (data) {
$.each(data.Search, function (index, value) {
if (index < 4) {
titlesForFunc.push(value.Title);
}
});
resolve(titlesForFunc);
},
function() { // need this to handle possible errors and complete the Promise
reject([]);
});
});
}
getSearchResults
is now async, getting the resulting array out of it would require you to do this:getSearchResults(val).then(arr => {
/*for each item in the array...*/
for (i = 0; i < arr.length; i++) {
/*check if the item starts with the same letters as the text field value:*/
/*create a DIV element for each matching element:*/
b = document.createElement("DIV");
b.innerHTML = arr[i];
/*insert a input field that will hold the current array item's value:*/
b.innerHTML += "<input type='hidden' value='" + arr[i] + "'>";
/*execute a function when someone clicks on the item value (DIV element):*/
b.addEventListener("click", function (e) {
/*insert the value for the autocomplete text field:*/
inp.value = this.getElementsByTagName("input")[0].value;
/*close the list of autocompleted values,
(or any other open lists of autocompleted values:*/
closeAllLists();
});
a.appendChild(b);
}
});
Hope it helps :)
Upvotes: 2