Dziś opublikowałem na swoim "blogu" kontrolkę wyszukiwania dynamicznego. Sam ją napisałem (w Javascripcie) – dlatego też mam z nią pewien problem. Otóż powiela mi wyniki.
Kontrolka działa tak, że po każdej wpisanej literze wysyła asynchronicznie nowe zapytanie GET do serwera, a następnie w odpowiedzi na nie wyświetla listę <ul>
z wynikami, usuwając wcześniejszą listę (za pomocą metody jQuery empty
).
Ale jak za szybko się wpisze kilka liter, to wyniki są powielone (kilka takich samych elementów <li>
). Teoretycznie przecież kod asynchroniczny działa w swoim czasie, a synchroniczny w swoim. Już prędzej oczekiwałbym, że wyników będzie za mało (bo odpowiedź nie zdąży dojść z serwera) niż za dużo.
Być może po jakimś czasie sam doszedłbym do tego, co to powoduje (może to nawet banalne?), ale myślę, że ktoś może szybciej to zrobi, mający więcej doświadczenia z AJAX-em. Nie jest to bardzo denerwująca cecha, ale jednak to nieprofesjonalne.
Załączam linki do kodu funkcji odpowiedzialnych za tę kontrolkę:
- https://github.com/silvuss/silvuss.github.io/blob/master/js/display-search-results.js
- https://github.com/silvuss/silvuss.github.io/blob/master/js/search.js
PS: Tak, wiem, że jest to napisane dość nieciekawie ;) (na razie). Wszelkie sugestie co do ulepszenia bardzo chętnie przyjmę.
UPDATE: Jeśli ktoś chciałby, to wkleję też cały kod tutaj:
search.js
// onblur
function hideResults(event) {
if (event.relatedTarget === null ||
!event.relatedTarget.classList.contains("search-form__results-list__item__link")) {
$(".search-form__results-list").hide();
}
}
// onfocus
function showResults() {
const resultsElement = $(".search-form__results-list");
if (resultsElement.text() != "") {
resultsElement.show();
}
}
// onkeyup
function search(noResultsText) {
const resultsElement = $(".search-form__results-list");
resultsElement.empty();
if ($(".search-form__input").val() != "") {
displaySearchResults(noResultsText);
} else {
resultsElement.hide();
}
}
display-search-results.js
function displaySearchResults(noResultsText) {
const input = $(".search-form__input");
const resultsElement = $(".search-form__results-list");
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
const data = this.response;
const xml = new DOMParser().parseFromString(data, "text/xml");
const results = [];
const articles = xml.getElementsByTagName("item");
const query = new RegExp(input.val(), 'gi');
for (let article of articles) {
const titleElement = article.getElementsByTagName("title")[0];
const title = titleElement.textContent;
const linkElement = article.getElementsByTagName("link")[0];
const link = linkElement.textContent;
if (title.match(query)) {
results.push({
title,
link
});
}
}
if (results.length != 0) {
for (let r of results) {
const rLink = document.createElement("a");
rLink.setAttribute("href", r.link);
rLink.append(r.title);
rLink.classList.add("search-form__results-list__item__link");
const rElement = document.createElement("li");
rElement.append(rLink);
rElement.classList.add("search-form__results-list__item");
resultsElement.append(rElement);
}
} else {
const errorElement = document.createTextNode(noResultsText);
const rElement = document.createElement("li");
rElement.append(errorElement);
rElement.classList.add("search-form__results-list__item", "search-form__results-list__item--error");
resultsElement.append(rElement);
}
resultsElement.show();
}
};
xhttp.open("GET", "/feed.xml", true);
xhttp.send();
}