Rychlá kontrola elementu v jQuery

Ověřit, že element, který jste našli, je ten, který jste hledali, se dá v jQuery jednoduše ověřit pomocí funkce is():

if (el.parent().is('.menu')) {
    el.addClass('menu-item');
}

Problém je ale v tom, že funkce is() je dost pomalá a její použití výrazně zpomaluje prováděný kód.

 

Funkce is() totiž musí nejprve rozparsovat a pochopit zadaný řetězec (např. v předchozím příkladu, že vás zajímá, zda má prvek CSS třídu menu), následně najít všechny příslušné prvky a porovnat, jestli je mezi nimi ten, na který se ptáte – což jsou dvě nejnáročnější operace v jQuery vůbec.

Proč to tedy jQuery neusnadnit a nezeptat se rovnou na to, co vás zajímá?

Třeba zrovna v případě CSS třídy se nabízí mnohem lepší a rychlejší metoda hasClass():

if (el.parent().hasClass('menu')) {
    el.addClass('menu-item');
}

V tomto případě jQuery rovnou ví, že vás zajímá CSS třída a navíc ví, na kterém prvku, takže pouze projde ClassList (nebo ClassName u starších prohlížečů) a podívá se, jestli tam hledaná třída je nebo není.

A jak ukazují výkonové testy, přímá kontrola třídy může být až 10x rychlejší než ověření pomocí is().

Stejně tak pokud vás zajímá konkrétní atribut, jako id, type, href apod., můžete se zeptat přímo metody attr() nebo prop() místo složitého vyhledávání přes is():

el.is('#menu'); //pomalé
el.attr('id') === 'menu'; //rychlé

el.is('input'); //pomalé
el.prop('tagName') === 'INPUT'; //rychlé

el.is('[type=password]'); //pomalé
el.attr('type') === 'password'; //rychlé

Všechno nebo nic

Další případ pomalého hledání nastává v okamžiku, kdy k prvku hledáte konkrétního rodiče (nebo jiného příbuzného). Např. chcete ověřit, že je prvek uvnitř menu, ale nevíte, na jaké úrovni (takže nelze použít výše uvedený příklad s přímým rodičem).

//hodně pomalé
isMenuItem = $el.parents('.menu').length > 0;
//pomalé
isMenuItem = $el.closest('.menu').length > 0;
//rychlé
isMenuItem = (($el.prop('tagName') === 'LI'
   || $el.prop('tagName') === 'A')
   && $el.closest('ul').hasClass('.menu'));

První řádek kontroluje, jestli alespoň jeden rodič prvku má třídu menu. Což ovšem znamená, že musí projít všechny rodiče až to rootu, a to je pomalé. Druhý příkaz je urychlen tím, že používá metodu closest(), která zastaví po nalezení prvního odpovídajícího elementu, takže v případě, že je prvek skutečně uvnitř menu, nemusí jít až do rootu. Ale pokud to prvek menu není, stále musí projít vše až do rootu, aby se o tom přesvědčila.

Třetí příkaz je nejrychlejší, protože využívá konkrétní situace, kdy menu je tvořeno pomocí UL-LI-A struktury a tak může nejprve ověřit, že prvek je LI nebo odkaz (z nichž se menu skládá) a nebude tedy procházet DOM pro DIVy a SPANy, které v menu vůbec nejsou. Následně, protože ví, že LI může být jen uvnitř prvku UL, najde nejbližší UL a jednoduše ověří, jestli má třídu menu; pokud ji nemá, program ví, že to není součást menu a nemusí už dál hledat.

To IS or not to IS

Samozřejmě i funkce is() má své opodstatnění, a to hlavně ve složitějších případech, kdy nelze jednoduše určit konkrétní hodnota, podle které se dá prvek určit (nebo by takové určení bylo příliš zdlouhavé a nepřehledné:

//pomalejší, ale přehledné
isInput = el.is('input,select,textarea');

//rychlejší, ale ne tak přehledné
isInput = (el.prop('tagName') === 'INPUT'
        || el.prop('tagName') === 'SELECT'
        || el.prop('tagName') === 'TEXTAREA');

A co teprve tenhle případ:

//pomalejší, ale velmi přehledné
isInput = el.is('input[type=text],'
          + 'input[type=password],textarea');

//rychlejší, ale úplně nepřehledné
isInput = ((el.prop('tagName') === 'INPUT'
       && (el.attr('type') === 'text'
       ||  el.attr('type') === 'password'))
       ||  el.prop('tagName') === 'TEXTAREA');

V posledním případě budete mít problém jen správně zapsat závorky a velikost písmen a při každé změně hrozí, že něco z toho popletete.

A samozřejmě nesmíme zapomínat na tenhle případ:

isVisible = el.is(':visible');

Tady by se ověření viditelnosti prvku dělalo jen těžko, vzhledem k tomu, co všechno ji může ovlivňovat (viditelnost rodičů, jejich rozměry, overflow, atd.)

Napsat komentář

Vaše emailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *

Tato stránka používá Akismet k omezení spamu. Podívejte se, jak vaše data z komentářů zpracováváme..