Reputation: 421
I'm creating a search results autocomplete like Google has.
The results (or source) of the autocomplete is gonna be a dynamic array (the array was created using object accessed from the URL
property in the $.ajax
call) and the elements change very quickly on each keypress
event.
What I mean is pretty simple:
autocompleteResults
$.ajax
success callback function we assign this variable to the empty array: autocompleteResults = []
After this, we're executing the $.each()
function:
$.each(d.query.pages, function(i) {
autocompleteResults.push(d.query.pages[i].title);
});
// d.query.pages - JSON object from the success callback (success: function(d) {...}
We're calling callback(autocompleteResults)
at the end of the success callback
Calling this function works and the array is returned correctly:
getAutocompleteResults(function() {
console.log(autocompleteResults);
});
Then in the same scope:
$("#search").autocomplete({source: autocompleteResults},...);
And it works only for the second time I type something.
Quick Overview using images:
All my research results about this problem:
Autocomplete works perfectly if it isn't placed in the keypress
event function. However, it that case, we get the results once and then it stops responding to the future types (entering "a" - 5 results, entering "aa" - same results as it was "a" and it doesn't respond.
I thought I should change the source again using the $( ".selector" ).autocomplete( "search", "" );
as stated here but I'm not sure if it would fix this problem.
My code somehow stacks up AJAX requests and the same request is repeated many times. How could I fix this?:
Threads like these didn't help: jQuery UI autocomplete with JSON, jQuery autocomplete with callback ajax json, Jquery Autocomplete Search with Ajax Request as sourcedata, https://forum.jquery.com/topic/autocomplete-with-ajax-and-json-not-working.
To see the full project code go here: https://codepen.io/Kestis500/pen/PErONL?editors=0010
Or use a small snippet of the code (so you can see the most important part where the problem happens):
$(function() {
var autocompleteResults;
var changeText2 = function(e) {
var request = $("input").val() + String.fromCharCode(e.which);
$("#instant-search").text(request);
var getAutocompleteResults = function(callback) {
$.ajax({
url: "https://en.wikipedia.org/w/api.php?action=query&format=json&gsrlimit=5&generator=search&origin=*&gsrsearch=" + $('#instant-search').text(),
success: function(d) {
autocompleteResults = [];
$.each(d.query.pages, function(i) {
autocompleteResults.push(d.query.pages[i].title);
});
callback(autocompleteResults);
},
datatype: "json",
cache: false
});
};
getAutocompleteResults(function() {
console.log(autocompleteResults);
});
$("#search").autocomplete({
source: autocompleteResults,
response: function() {
if (
$("#instant-search")
.text()
) {
$("table").css("display", "table");
}
},
close: function() {
if (!$(".ui-autocomplete").is(":visible")) {
$(".ui-autocomplete").show();
}
},
appendTo: ".input",
focus: function(e) {
e.preventDefault();
},
delay: 0
});
};
var changeText1 = function(e) {
if (
/[-a-z0-90áãâäàéêëèíîïìóõôöòúûüùçñ!@#$%^&*()_+|~=`{}\[\]:";'<>?,.\s\/]+/gi.test(
String.fromCharCode(e.which)
)
) {
$("input").on("keypress", changeText2);
}
// THIS PART HAS NOTHING TO DO WITH THIS PROBLEM AND DELETING THIS WOULD MAKE A TABLE CREATED BY THE HTML TO NOT FUNCTION
var getInputSelection = function(input) {
var start = 0,
end = 0;
input.focus();
if (
typeof input.selectionStart == "number" &&
typeof input.selectionEnd == "number"
) {
start = input.selectionStart;
end = input.selectionEnd;
} else if (document.selection && document.selection.createRange) {
var range = document.selection.createRange();
if (range) {
var inputRange = input.createTextRange();
var workingRange = inputRange.duplicate();
var bookmark = range.getBookmark();
inputRange.moveToBookmark(bookmark);
workingRange.setEndPoint("EndToEnd", inputRange);
end = workingRange.text.length;
workingRange.setEndPoint("EndToStart", inputRange);
start = workingRange.text.length;
}
}
return {
start: start,
end: end,
length: end - start
};
};
switch (e.key) {
case "Backspace":
case "Delete":
e = e || window.event;
var keyCode = e.keyCode;
var deleteKey = keyCode == 46;
var sel, deletedText, val;
val = this.value;
sel = getInputSelection(this);
if (sel.length) {
// 0 kai paprastai trini po viena o 1 ar daugiau kai select su pele trini
$("#instant-search").text(
val.substr(0, sel.start) + val.substr(sel.end)
);
} else {
$("#instant-search").text(
val.substr(0, deleteKey ? sel.start : sel.start - 1) +
val.substr(deleteKey ? sel.end + 1 : sel.end)
);
}
break;
case "Enter":
if (
$("#instant-search")
.text()
.trim()
) {
console.log("Redirecting...");
}
break;
}
if (!$("#instant-search")
.text()
.trim()
) {
$("table, .ui-autocomplete").hide();
}
};
$("input").on("keydown", changeText1);
$("input").on("input", function(e) {
$("#instant-search").text($("#search").val());
if (
$("#instant-search")
.text()
.trim()
) {
$('table').css('display', 'table');
} else {
$("table").hide();
}
});
});
html,
body {
height: 100%;
width: 100%;
}
body {
margin: 0;
padding: 0;
font-family: sans-serif;
background-color: #000428;
/* fallback for old browsers */
background-image: -webkit-linear-gradient(to right, #004e92, #000428);
/* Chrome 10-25, Safari 5.1-6 */
background-image: linear-gradient(to right, #004e92, #000428);
/* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
}
.v-container {
display: table;
height: 100%;
width: 100%;
}
.v-content {
display: table-cell;
vertical-align: middle;
}
.text-center {
text-align: center;
}
h1 {
color: #fff;
}
.input {
overflow: hidden;
white-space: nowrap;
}
.input input#search {
width: calc(100% - 30px);
height: 50px;
border: none;
font-size: 10pt;
float: left;
color: #4f5b66;
padding: 0 15px;
outline: none;
}
.input button.icon {
border: none;
height: 50px;
width: 50px;
color: #4f5b66;
background-color: #fff;
border-left: 1px solid #ddd;
margin-left: -50px;
outline: none;
cursor: pointer;
display: none;
-webkit-transition: background-color .5s;
transition: background-color .5s;
}
.input button.icon:hover {
background-color: #eee;
}
.ui-autocomplete {
list-style: none;
background-color: #fff;
-webkit-user-select: none;
user-select: none;
padding: 0;
margin: 0;
width: 100% !important;
top: auto !important;
display: table;
table-layout: fixed;
}
.ui-helper-hidden-accessible {
display: none;
}
.autocomplete-first-field {
width: 15%;
display: inline-block;
}
.autocomplete-second-field {
width: 85%;
display: inline-block;
text-align: left;
vertical-align: middle;
}
.three-dots {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
table {
width: 100%;
border-spacing: 0;
border-collapse: collapse;
display: none;
table-layout: fixed;
}
table tr {
background-color: #fff;
-webkit-user-select: none;
user-select: none;
}
tr:first-child {
background-color: #ffc800;
color: #fff;
}
table td,
.ui-menu-item-wrapper {
padding: 10px 0;
}
td:nth-child(2) {
width: 85%;
text-align: left;
}
.ui-menu-item,
table {
cursor: pointer;
}
:focus {
outline: 0;
}
a {
text-decoration: none;
color: #fff;
position: relative;
}
#random-article {
margin-bottom: 5px;
}
.search-results {
background: #fff;
margin-top: 50px;
border-left: 0 solid;
cursor: pointer;
-webkit-transition: border-left .5s;
transition: border-left .5s;
}
.search-results h4,
.search-results p {
margin: 0;
padding: 10px;
}
.search-results:not(:first-child) {
margin-top: 25px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!-->
<html class="no-js">
<!--<![endif]-->
<div class="v-container">
<div class="v-content text-center">
<div id="random-article"><a href="https://en.wikipedia.org/wiki/Special:Random">Click here for a random WikiSearch article! :)</a></div>
<div class="input">
<input type="text" id="search" placeholder="Search...">
<button class="icon"><i class="fa fa-search"></i></button>
<table>
<tr>
<td class="fa fa-search">
<td id="instant-search" class="three-dots"></td>
</tr>
</table>
</div>
</div>
</div>
If you have any ideas how to fix this problem your help would be very appreciated :)
Upvotes: 1
Views: 2171
Reputation: 1677
You have kept source: autocompleteResults,
where you autocompleteResults is dynamically changing you should have done some thing like this
source: function (request, response) {
getAutocompleteResults(function(data){
response(data);
});
},
working code https://codepen.io/anon/pen/OzeQvw?editors=0010
Upvotes: 2