Přejít k navigační liště

Zdroják » JavaScript » JavaScript Restart – QuerySelector

JavaScript Restart – QuerySelector

Články JavaScript

Setkávám se často s vývojáři jejichž první úvaha nad implementací začíná napsáním “$”. jQuery je všudypřítomné, a pro tyto vývojáře je překvapení, že se dá psát bez něj.

Nálepky:

Na obzoru je vydání nové verze Internet Exploreru nebo dokonce nového prohlížeče od Microsoftu a tudíž je prakticky neudržitelná podpora IE 8 i pro velké korporátní společnosti. Když jsem začal zjišťovat, co si budeme moci při psaní webových aplikací dovolit, uvědomil jsem si, že je možná na čase se rozloučit se všudypřítomným jQuery, jelikož do JavaScriptu (a souvisejících API) s podporou ES5 přibylo nemálo užitečných funkcí.

Pojďme tedy v tomto seriálu udělat restart a dát čistému Javascriptu ještě šanci.

Jak najít element v DOM postaru

Před dávnými a dávnými věky, milí vývojáři, byla modlou všech JavaScriptových vývojářů tato funkce:

document.getElementById('id');

a ta sloužila (a slouží) k nalezení elementu podle jeho id. (Pokud není nalezen žádný element, getElementById vrací hodnotu null.)

document.getElementById() má ještě několik příbuzných funkcí:

  • document.getElementsByTagName()
  • document.getElementsByName()
  • document.getElementsByClassName()

které vrací pole elementů (popř. prázdné pole, není-li nalezeno nic), leč ty se vyskytovaly v kódu řádově méně častěji než výše zmíněná funkce document.getElementById().

Nelze zde nevzpomenout na legendární funkci document.all, zavedenou Internet Explorerem (V současné době není doporučeno ji používat a v IE11 už dokonce nefunguje). Tato funkce byla námětem mnoha učených disputací a flamewarů.

document.all['id'];

Tato funkce byla natolik oblíbená, že ji s velkým odporem implementoval i Firefox, což způsobilo kurozní situaci. Totiž test pomocí:

if (document.all) {}

se zvesela používal pro detekci Internet Exploreru, což, jak už dnes víme, je velmi, velmi ošklivé. Jenže jelikož Firefox nechtěl být detekován jako Explorer a zároveň chtěl umět document.all, musel tento test vracet vždy hodnotu false.

Pokud přesto po něčem takovém jako document.all toužíte, podobný výsledek dostanete při zavolání:

document.getElementsByTagName('*')

Jak najít element pomocí CSS selektorů v jQuery

Když se řekne jQuery, každý si představí dotazování na DOM pomocí CSS selektorů (přesto, že jQuery toho umí mnohem víc). DOM query pomocí CSS je báječný nápad, protože každý vývojář webových aplikací CSS musí znát a pracuje s ním prakticky denně.

Např:

var element = $('body div');

kde výstupem je pole elementů vyhovující zadanému dotazu. Podobně lze výraz napsat takto

var element = $('div', 'body');

kde druhým parametrem říkáme, že se dotaz se má omezit pouze na body – to bývá velmi užitečné, pokud chceme, aby naše komponenta nemohla ovlivnit ostatní. Velmi doporučuji používat.

V případě, že element není nalezen, dotaz vrací prázdné pole [].

CSS selectory v DOM

Pole všech elementů vyhovujících selectoru dostaneme takto

document.querySelectorAll('body div');

což je vlastně ekvivalentem dotazu v jQuery.

Dotaz nevrací pole, jak by se dalo po vzoru jQuery čekat, ale NodeList, což způsobí, že jej lze sice procházet jako pole (má totiž metodu item), bohužel další metody jako např.forEach nemá.
Pokud ovšem touha po polích neustává, lze to vyřešit takto:

var nodes = document.querySelectorAll('div'),
    divArray = [].slice.call(nodes);

V ES 6 je to jednodušší, díky metodě Array.from, která je zatím implementována jenom ve Firefoxu:

var divArray = Array.from(div);

Pokud má být výsledkem pouze jeden element, pak použijeme jinou funkci:

document.querySelector('body div');

a výsledek je první nalezený element. Pokud dojde k situaci, že element není nalezen, document.querySelector vrací hodnotunull.

Zároveň je možno zadat skupinu selektorů (stejně jako v jQuery):

document.querySelector('#element1, #element2');
document.querySelectorAll('#element1, #element2');

pak výsledkem bude pro document.querySelector první nalezený element, pro document.querySelectorAll pak NodeList s elementy.

Zde je třeba upozornit na jeden rozdíl oproti jQuery, srovnejte:

$('');                          => []
document.querySelector('');     => DOMException
document.querySelectorAll('');  => DOMException

jQuery vrací stále prázdné pole (ostatně pořád pracuje s polem elementů), zatímco querySelector v případně prázdného dotazu, vyhodí výjimku DOMException. Na to je třeba myslet a nezapomínat.

QuerySelector a querySelectorAll jsou zároveň metody, které dostal “do vínku” každý DOM element, tudíž, pokud potřebujeme vyhledávat v potomcích elementu, pak

var element = $('div', 'body');

můžeme nahradit

var element = document.querySelector('body').querySelectAll('div');

Nativní funkce jsou logicky výkonnější než funkce jQuery, což je jistě dobrý důvod k jejich použití.

Pojďme se podívat, co se stane, když CSS selektor nebude syntakticky správně:

var uglyQuery = 'div [';

$(uglyQuery);
document.querySelectorAll(uglyQuery);

pokud předpokládáte, že oba selectory skončí výjimkou, pak předpokládáte správně, jen je třeba si uvědomit, že jQuery vyhodí obecnou výjimku Error, zatímco document.querySelector pochopitelně vyhodí výjimku DOMException.

Zápis document.querySelectorAll je prostě dlouhý a nebudu se vůbec divit, bude-li se zkracovat. Je to funkce jako každá jiná, řekneme si, takže ji jen stačí přiřadit

var q = document.querySelectorAll;

ale ouha, dojde k výjimce, a to k výjimce Illegal invocation, která se nevidí každý den.

Pro moderní prohlížeče a IE9+ je řešení docela elegantní:

var q = document.querySelector.bind(document);

pro prohlížeče nepodporující metodu bind pak nezbývá než udělat toto:

var q = function(q) {
    return document.querySelector(q);
};

ClassList

Milým objevem (pro mne) je, že pro operace s třídami jako jako je addClass, removeClass, toggleClass nepotřebuji jQuery a taktéž na starý způsob s nastavováním řetězce classNameuž můžu zapomenout.

Nejdříve postaru:

// addClass
element.className += ' active';
// removeClass
element.className = element.className.replace('active', '').trim();

Funkci toggle si doplní laskavý čtenář sám, já už to, doufám, nikdy nebudu muset psát.

V jQuery to dokážeme o poznání elegantněji

$(element).addClass('active');
$(element).removeClass('active');
$(element).toggleClass('active');
$(element).hasClass('active');

Od IE 10 lze použít toto:

element.classList.add('active');
element.classList.remove('active');
element.classList.toggle('active');
element.classList.contains('active');

Na první pohled by se mohlo zdát, že classList obsahuje pole, ale Array takové metody jako toggle nemá. Je to tím, že classList vrací DOMTokenList, datový typ pro hodnoty v DOM, oddělované mezerou.

Perlička na závěr

Při ověřovaní informací výše uvedených, jsem narazil na funkci elementu s podivným názvem insertAdjacentHTML. Domníval jsem se že jde o novinku, a tak mě překvapilo, že je implementována v IE4.

Druhým překvapením bylo, že by měla být o poznání výkonnější než nastavení vlastnosti innerHTML.

Použít se dá místo:

$(el).after(html);

a to takto:

el.insertAdjacentHTML('afterend', html);

Inu člověk se učí pořád.

Komentáře

Subscribe
Upozornit na
guest
15 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
DavidGrudl

V článku je jedna nepřesnost: funkce jQuery (nebo častěji $) nevrací pole, nýbrž objekt jQuery.

Dojem, že $ vrací pole, může podpořil Chrome Developer Tools, který (nepochopitelně) v konzoli objekty jQuery vypisuje úplně stejně, jako by šlo o pole. Proto třeba na školeních jQuery jsem vždycky trval raději na používání Firebugu, který to vypisuje správně.

Objekt jQuery ukládá podobně jako pole prvky pod číselné klíče a má proměnnou length, tudíž se s ním dá trošku jako s polem pracovat (říká se tomu array-like object), ale další metody, jako například v článku zmíněnou forEach, nemá ani jQuery. Pro převod jQuery na pole slouží metoda toArray().

Ale jinak fajn článek. jQuery splnilo svou důležitou historickou úlohu a většinu jeho někdejších úloh dnes zvládá nativní DOM. jQuery je dnes knihovna pro AJAXové requesty a promise pattern ;-)

Daniel

Dev tools to loguje ako array preto, lebo ma v prototype ulozenu array.prototype.splice.

https://gist.github.com/danielhusar/6030dcf3e615e7f482f1

Jakub Vrána

Pár dalších nepřesností:

  1. element.className.replace('active', '').trim(); odstraní nejen třídu active, ale i active ze všeho, co to obsahuje jako podřetězec. Obvykle se používalo něco jako .replace(/(^| )active( |$)/g, ' ').
  2. document.all není funkce, ale vlastnost.
  3. Pole se nevyznačují tím, že by měly metodu item, ale tím, že mají vlastnost length.
steida

Ani pro ten promise pattern není, protože jQuery ho zkurvila a nikdy neopraví. tl;dr Nepracuje vůbec s chybama.

dherbolt

Funkce document.getElementsBy* nevrací pole, ale HTMLCollection. Tato kolekce má sice propertu length a k prvkům je možné přistupovat přes index (podpbně jako v případě objektu jQuery), ale oproti nim je HTMLCollection „živá“. Kolekce je automaticky aktualizována pokud dojde ke změně v dokumentu.

var els; 

document.body.appendChild(document.createElement('div'));
els = document.getElementsByTagName('div');
els.length; // 1

document.body.appendChild(document.createElement('div'));
els.length; // 2

Funkce document.querySelectorAll vrací statický NodeList. V tomto případě nedochází k automatické aktualizaci. Zde je nutné upozornit také na existenci „živého“ NodeList. V tomto typu jsou uloženy například childNodes.

smokie.mt

Chapem motivaciu zanechat jQuery a vratit sa k nativnym funkciam Javascriptu, ale prave jedna z vlastnosti, pre ktore je jQuery tak oblubene je moznost venovat sa inym veciam, nez mysliet na to, ci ta a ta implementacia bude podporovana v tom a tom prehliadaci.

A ked som si pocas citania clanku uvedomil, ze asi polovica textu je venovana tomu ako treba to a to volanie upravit aby bolo podporovane v tom a tom prehliadaci, tak som si povedal, ze toto nebude pre mna.

DavidGrudl

Všechno v článku funguje od IE 10, tady na cca 90-95 % prohlížečů. Tedy pro tyto nejčastější úkoly už pomalu přestává být jQuery potřeba. Na některých webech už dnes, na jiných brzy.

Jakub

No právě, IE10 je už hodně velký luxus. Běžně se setkávám spíše s požadavky na IE9, občas i IE8. Často i na WinXP. Situace ve velkých korporátech je smutná. A může jít klidně i o veřejný web, ale když si ho ředitěl se svou IE8 neprohlídne, tak je úplně jedno, že 95% zbytku světa ano :)
I když není vše tak špatné, třeba jeden zákazník teď od nového roku IE8 upustil a začal používat rovnou IE11.

Lemming

Souhlas. Děláme veřejný web, ředitel IE 8 nemá, ale zato business má statistiku prohlížečů uživatelů. Teprve asi před rokem jsem prosadil, že kulaté rohy se už konečně mohou dělat přes border-radius a ne obrázky, ale pořád platí, že uživatel s IE 8 musí mít plnohodnotný přístup k aplikaci, byť s povolenými grafickými odlišnostmi, jako třeba ty nekulaté rohy.

Na podzim jsme dělali jedno zobrazení, na které by bylo super inline SVG. Bohužel, to by nefungovalo v IE 8, takže to neprošlo a musím to kreslit Javascriptem v Raphael… :(

Tak holt vypadá komerční realita mimo technologická dema a startupy :) Když mám na webu pár návštěvníků, tak se s IE 9 / IE 8 patlat nevyplatí, ale pro navštěvovanější weby dávají v absolutních číslech pořád dost lidí, aby se vyplatilo kvůli tomu vývojáře prudit.

Martin

Ve finale stejne clovek potrebuje nejaky obal na tu nativni API, protoze ta API je skvela v tom, ze dava nekonecne moznosti pouziti, ale chtel bych si implementovat chainovani, atd… Kazdopadne v IE10+ je to otazka cca jednoho dne napsat si knihovnicku na selector a manipulaci s nody. Stejne tak ajaxy nejsou zadna veda s XHR a FormData.

Jen bych jeste dodal k clanku, ze jak insertAdjacentHTML tak innerHTML je akorat tak cesta pro XSS a navic budu mit nekde v stringu seredny HTML. Takze pokud to delam u klienta, tak si to muzu krasne sestavit z document.createElement, document.createTextNode a pokud si posilam HTML, ze serveru tak bych si mel byt sakra jisty co tam mam, nez to takhle vlozim do stranky.

helb

Docela šikovný web pro rychlé oživení „čistého“ javascriptu po letech používání jQuery – http://youmightnotneedjquery.com/

xxar3s

Ten clanok prave krasne ukazuje ze JQuery ma zmysel. Kazdy zapis v JQuery je kratsi ako v nativnom HTML a tym padom aj prehladnejsi… Od toho su kniznice, aby sme zbytocne nepisali kod, ktory uz napisal niekto iny, ale venovali sa podstate problemu, ktory riesime.

Zdeněk Kopš

Faktická: není to náhodou tak, že knihovna Sizzle (se kterou pracuje jQuery) používá querySelectorAll pokud je dostupné? řádek na Gitu jako důkaz…

Tím se ale nesnažim tvrdit, že by práce s nativním JS nebyla rychlejší. I kdyby byly ve výsledku použity stejné metody, knihovny budou mít vždy režii navíc. Na druhou stranu knihovny se těší podobnějšímu chování napříč prohlížečovým spektrem, člověk se pak nemusí starat o vyjímky a fallbacky.

Pokud jde ale v prvé řadě o výkon, není těžké si jednoduchý fallback napsat:
if(!document.querySelectorAll) document.querySelectorAll = Sizzle;

Dash

Vzhledem k tomu, že jQuery 2 většinu z těchto novinek používá, tak nevidím důvod jej nepoužít. Minimálně kvůli větší přehlednosti a úspornosti kódu.

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.