TIP: Jak si napsat našeptávač
Našeptávače jsou velice užitečné techniky, jak uživateli pomoci při zadávání dotazu do vyhledávání. Jejich implementace přitom nemusí být ani moc náročná, stačí se jen řídit základním pravidlem „Oddělovat obsah od formy“ a nekomplikovat JS zbytečně stylováním výsledku, když to pohodlně může obstarat externí CSS.
Na ukázku jsem připravil jednoduchou implementaci, která snad poskytne vodítko při tvorbě vašeho vlastního našeptávače. Jde skutečně o jednoduchou variantu, která vypisuje jen seznam slov (mohou se hodit i nějaké další informace) a vůbec neřeší nějaký timeout pro dotazování při psaní slova (dotazuje se po každém stisku klávesy v textovém poli, což zbytečně zatěžuje server).
Ukázka použití:
Jak to celé funguje?:
Našeptávače používají k získávání informací ze serveru XMLHttpRequest. Jednoduchá třída zapouzdřující jednotlivé techniky pro různé prohlížeče může vypadat např. takto:
HttpRequest
/**
* HttpRequest object
*/
function HttpRequest() {
this.id = this.httpRequests.length;
this.httpRequests[this.id] = this;
};
HttpRequest.prototype.id = null;
HttpRequest.prototype.httpRequests = []; /* Store all instances */
HttpRequest.prototype.httpRequest = null;
HttpRequest.prototype.type = "GET";
HttpRequest.prototype.url = null;
HttpRequest.prototype.parameters = null;
HttpRequest.prototype.action = null;
HttpRequest.prototype.make = function() {
if (window.XMLHttpRequest) { // Mozilla, Safari,...
this.httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
try {
this.httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
this.httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {}
}
}
if (this.httpRequest) {
this.httpRequest.open(this.type, this.url, true);
eval("this.httpRequest.onreadystatechange = function() { HttpRequest.prototype.httpRequests["+this.id+"].procesResponse(); };");
if (this.type == "POST") {
this.httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
this.httpRequest.setRequestHeader("Content-length", this.parameters.length);
this.httpRequest.setRequestHeader("Connection", "close");
}
this.httpRequest.send(this.parameters);
} else {
alert('Cannot create XMLHTTP instance');
return false;
}
};
HttpRequest.prototype.procesResponse = function() {
if (this.httpRequest.readyState == 4) {
if (this.httpRequest.status == 200) {
this.action(this.httpRequest.responseText);
} else {
alert('There was a problem with the request.');
}
}
};
HttpRequest.prototype.setType = function(type) {
this.type = type;
};
HttpRequest.prototype.setUrl = function(url) {
this.url = url;
};
HttpRequest.prototype.setParameters = function(parameters) {
this.parameters = parameters;
};
HttpRequest.prototype.setAction = function(action) {
this.action = action;
};
/******************************************************************************/
Server může na dotaz odpovědět jakkoli, ale nejběžnější formy odpovědi jsou XML a JSON (JavaScript Object Notation). XML odpověď má výhodu v tom, že se dají takovéto výsledky použít i k jiným účelům (získávání dat jinými servery apod.). JSON je zase výhodnější použít při volání z JS (odpověď v podobě objektu se dá přiřadit nějaké proměnné a s ní pak pohodlně pracovat). Proto zde používám raději JSON — server vrací pole výsledných slov.
Našeptávač pak použije HttpRequest k získání relevantních informací ze serveru a jejich vypsaní uživateli. Je dobré data vypisovat např. jako seznam, který posléze můžeme pomocí CSS patřičně nastylovat. Ukázkový našeptávač vypadá takto:
Whisperer
/**
* Whisperer object
*/
function Whisperer(textItem, url) {
this.id = this.whisperers.length;
this.whisperers[this.id] = this;
this.textItem = textItem;
this.url = url;
eval("document.getElementById(this.textItem).onkeyup = function() { Whisperer.prototype.whisperers["+this.id+"].refresh(); };");
eval("document.getElementById(this.textItem).onblur = function() { setTimeout(function() { document.getElementById('"+this.textItem+"__whisperer').style.display='none'; }, 500); };");
document.getElementById(this.textItem).onfocus = function() { document.getElementById(this.id+"__whisperer").style.display=''; };
var list = document.createElement("div");
var ul = document.createElement("ul");
list.id = this.textItem+'__whisperer';
list.style.left = document.getElementById(this.textItem).offsetLeft+"px";
list.style.width = document.getElementById(this.textItem).offsetWidth+"px";
list.style.top = (document.getElementById(this.textItem).offsetTop + document.getElementById(this.textItem).offsetHeight)+"px";
list.style.display = 'none';
list.appendChild(ul);
document.getElementById(this.textItem).parentNode.appendChild(list);
}
Whisperer.prototype.id = null;
Whisperer.prototype.url = "";
Whisperer.prototype.whisperers = []; /* Store all instances */
Whisperer.prototype.words = []; /* Words cache */
Whisperer.prototype.refresh = function() {
function writeContent(whisperer, word) {
if (word == document.getElementById(Whisperer.prototype.whisperers[whisperer].textItem).value) {
var output = document.getElementById(Whisperer.prototype.whisperers[whisperer].textItem+'__whisperer').firstChild;
while(output.childNodes.length)
output.removeChild(output.childNodes[0]);
for(i in Whisperer.prototype.words[word]) {
var li = document.createElement('li');
li.appendChild(document.createTextNode(Whisperer.prototype.words[word][i]));
eval("li.onclick = function() { document.getElementById(Whisperer.prototype.whisperers["+whisperer+"].textItem).value = this.innerHTML; };");
output.appendChild(li);
}
}
}
var word = document.getElementById(this.textItem).value;
if (Whisperer.prototype.words[word]) {
writeContent(this.id, word);
} else if (word != "") {
var request = new HttpRequest();
request.setUrl(this.url+encodeURIComponent(word));
eval("request.setAction(function(response) {"
+" eval('Whisperer.prototype.words[\""+word+"\"] = ' + response);"
+" writeContent('"+this.id+"', '"+word+"');"
+"});");
request.make();
}
};
/******************************************************************************/
Poté již stačí jen vytvořit objekt našeptávače s příslušným ID textového pole a URL adresou pro získání odpovědi a vše na klientské straně již funguje: new Whisperer("textItem", "/ukazky/naseptavac/?page=json&text=");
Na straně serveru je samozřejmě třeba mít skript generující patřičné odpovědi (našeptávaná slova) pro zadaný text (v tomto ukázkovém našeptávači server vrací tříprvkové pole, které obsahuje zadaný text doplněný o {A, B, C}).