Vyhledávání v JavaScriptu
Školení, která pořádám
Pokud nemáme na vyhledávání moc vysoké nároky, není moc složité ho na serveru implementovat. Třeba na tomto serveru se používá prosté:
<?php
mysql_query("SELECT * FROM clanky WHERE clanek LIKE '%" . mysql_real_escape_string($_GET["search"]) . "%'");
?>
Využívá se faktu, že všechna data jsou uložena v databázi, kam podle mě jedině patří. Pokud je dat v databázi větší množství, je možné využít např. fulltextové indexy. Pokud jsou texty i v souborech, můžeme je procházet třeba funkcí glob a text hledat např. funkcí strpos.
Co ale v případě, kdy služby serveru nelze použít? Texty z tohoto serveru je možné stáhnout a pracovat s nimi bez připojení k Internetu. Vyhledávat v nich jde potom samozřejmě třeba příkazem grep
nebo jiným obecným vyhledáváním souborů, z prostředí webového prohlížeče by to ale jistě bylo pohodlnější.
V moderních prohlížečích lze toto vyhledávání zajistit díky technologii AJAX:
<table cellpadding="3" cellspacing="0">
<tbody id="vysledky"></tbody>
</table>
<script type="text/javascript">
function obsluha(xmlhttp) {
if (xmlhttp.readyState == 4) {
var match = xmlhttp.responseText.match(/<h1><a href="(.*)">(.*)<\/a><\/h1>([^˙]*)<!-- konec textu -->/);
if (match && (match[2] + match[3]).search(search) > -1) {
nalezeno++;
var tr = document.getElementById('vysledky').appendChild(document.createElement("TR"));
tr.appendChild(document.createElement("TD")).innerHTML = '<a href="' + match[1] + '">' + match[2] + '</a>';
}
zbyva--;
if (!zbyva && !nalezeno) {
alert('Zadaný řetězec nebyl nalezen.');
}
}
}
var search = new RegExp(location.search.replace('?search=', ''), 'i');
var nalezeno = 0;
var zbyva = 1; // celkový počet prohledávaných souborů
send_xmlhttprequest(obsluha, 'GET', 'ajax.html');
// další soubory k prohledání
</script>
Prohlížeč začne při zavolání funkcí send_xmlhttprequest
paralelně prohledávat všechny soubory a do tabulky vypisovat odkazy na ty, kde byl hledaný text nalezen. Při vytváření tohoto kódu jsem narazil na několik nástrah:
- Nelze použít objekt responseXML, protože prohlížeč při načítání souboru z disku nepošle správné hlavičky.
- Pro výpis dat se pochopitelně nedá volat metoda document.write. Funkce
obsluha
se totiž nevolá z konkrétního místa dokumentu, ale volá ji sám prohlížeč při stažení dat.
- V IE mi nefungoval ani kód
document.getElementById('vysledky').innerHTML += '…'
, bylo nutné použít DOM.
- Pokud výsledky chceme vypisovat do tabulky, měla by tato tabulka obsahovat značku tbody. IE si ji totiž jinak domyslí a pracuje s ní i na úrovni DOMu, Firefox ne.
- Znak
.
v regulárním výrazu strefí jakýkoliv znak kromě konce řádku. Na rozdíl od PHP ale neexistuje modifikátor s
, který by toto chování změnil. Pokud se jako náhrada použije regulární výraz (.|\n)
, je kód v IE příšerně pomalý. Vyřešit je to možné tak, že se \n
před hledáním nahradí jiným znakem nebo se použije výčet všech možných znaků včetně znaku konce řádku (nebo negace nepoužitého znaku).
Přijde mi, že k řešení je potřeba se spíše prokličkovat přes řadu nástrah, z nichž největší je rozdílné chování prohlížečů.
Přijďte si o tomto tématu popovídat na školení JavaScript a AJAX.
Diskuse
Martin:
lol! Tenhle blog mě nikdy nezklame, sranda je tu větší než na thedailywtf.com! Autorovy nápady stojí za to. Vyhledávání offline pomocí AJAX přímo v kódu stránek..... cha cha cha cha
Programátor opravdu není ten, kdo umí řadit příkazy za sebe a rozchodit kdejakou nesmyslnou myšlenku...
Garçon:
Taky jsem se musel smát a říkal jsem si, že je to legrační blbost, ale pak jsem si řek, že třeba pro hledání v dokumentaci na disku to může být řešení.
Nevím, co ti na myšlence připadá nesmyslného. Uživatelé jsou zvyklí na hledání přímo na stránkách a když ho nejde realizovat pohodlně na straně serveru (ať už u zmiňované verze pro stažení nebo třeba u HTML nápovědy), tak popisuji, jak se dá realizovat v JavaScriptu.
Co převratného jsi nám vlastně mudrováním o "řazení příkazů za sebe" chtěl sdělit?
Spud:
Me to pripada jako velice elegantni reseni napriklad u CD prezentaci, supr!
l:
Je to dobre, to jen Martin je nejaky divny.
ATom:
Hezké. Jen aby někdo nezapomněl na předchozí články o SQL injection a fakt nedal do SQL příkazu jen $_GET[search] ;-)
Garçon:
Začnu číst patičky, slibuju. ;-)
JKa:
Možná by stálo za to zmínit i možnost jednoduchého vyhledávání na stránkách pomocí Google a modifikátoru site:www.example.com. U stránek, kde nejsou časté změny to může stačit.
Jakub Vrána :
Toto vyhledávání ale pochopitelně nejde použít v případě, kdy stránky nejsou veřejně dostupné a třeba se ani nezobrazují přes webový server. Právě na tento případ je totiž článek zaměřen.
Jiný Martin:
Martinovi z prvního postu: Vrátil jsem se k tomuto článku, jen abych napsal, že právě pomocí regexpů v JS má řešeno vyhledávání třeba nápověda k Adobe Photoshop.
Zkus příště trochu přemýšlet, než začneš zaklínat thedailywtf.
Lukas:
Ciste pro lajka, kde do toho scriptu vstupuje hledany retezec?
Danoha:
Taky by mě zajímalo.
Magnus:
Dobrý den.
Chtěl bych se zeptat, když odesílám a přijímám data ze serveru pomocí AJAXu, pak nelze daný soubor ochránit pomocí .htaccess (deny from all). Je nějaká možnost, jak by mi mohl útočník soubor poškodit?
Data v souboru ošetřuji například mysql_real_escape_string, pokud data vyhledávám z databáze, takže SQL injection se stát nemůže. Výpis textu ošetřuji pomocí htmlspecialchars() proti XSS.
Je důležitá nějaká další funkce?
Děkuji za odpověď, ochrana není zrovna má silná stránka. :-(
Magnus:
Děkuji za rychlou odpověď.
Ještě bych se rád zeptal, pokud v adresáři mám soubor .htaccess a v něm "deny from all", tak nemohu poslat přes AJAX data, protože mám zakázaný přístup do souboru.
Proto vždy používám podmínky
if (isset($_POST['odeslana_data'])) { // script }
, aby se při zadání názvu souboru přes URL vykreslila pouze bílá stránka (tzn. když nebyla odeslána žádná data pomocí metody POST, uložení do DB se neprovede).
Ale někde jsem se dočetl, že lze POSTem data odesílat i jinak, než přes webový formulář. Proto i ve scriptu zjišťuji, jestli může tento uživatel odeslat data (například zda je přihlášen, má práva apod.).
Stačilo by mi vědět, zda mohu kvůli bezpečnosti systému takto AJAX používat.
Omlouvám se za nejspíš zbytečné dotazy, ovšem v mém věku (15) je bohužel školení nepřípustné.
David:
Zdravím,
rád bych použil tento kód pro offline vyhledávání ve všech html souborech, které jsou v jedné složce text, nebo číslo, které zadám do inputu na hlavní stránce...
už jsem se v diskusi dočetl,že data ro hledání získám přes GET a search, teď už mi jen zbývá otázka, jak nastavit, ve které složce chci ty soubory prohledávat?
Děkuji
Jakub Vrána :
Skript neprohledává složku, ale jednotlivé soubory. Ty je potřeba vyjmenovat pomocí řádků send_xmlhttprequest(obsluha, 'GET', 'ajax.html'). Skript k výpisu adresářů nemá přístup.
Michal:
Zdravím. Chci se zeptat jestli se od doby kdy byl vydán tento článek něco zásadně změnilo. Podpora ze strany prohlížečů a pod. Lze nějak efektivně přes JS prohledávat v offline souborech ? Případně jestli jsou nějaké offline vyhledávací enginy, které byste doporučil.
Děkuji.
Vita:
Zdravím,
tak jsem si vytvořil index.htm ... nakopíroval do něj celý kód ajax.html ve fci send_xmlhttprequest() zmenil na moji stránku a spustil stránku v prohlížeči v tomto tvaru: index.htm?search="ajax" .... a nic se nezobrazilo... kde je chyba?
Jakub Vrána :
Možná by něco mohlo být v chybové konzoli prohlížeče.
Ondra:
Taky mi to nejde nějak rozjet.:-/
Diskuse je zrušena z důvodu spamu.