• Strona główna
  • Curriculum Vitae
  • O mnie
  • Przykład: Gramatyka w PHP
  • Przykład: Kompresja CSS
  • Przykład: Kompresja JavaScript
  • Przykład: Skracanie linków
  • Przykład: Wykrywanie serwera HTTP
  • Przykład: Własna bramka SMS
  • Mapa strony
  • Kontakt
Niebieski Pomarańczowy Zielony Różowy Fioletowy

Własne selektory w jQuery

Opublikowane 16 sierpnia 2011. Autor: Kamil Brenk. Wizyt: 2 185.

Kategorie: JavaScript
Tematyka: JavaScript, jQuery w praktyce

sie 16

Frameworka jQuery nie trzeba nikomu przedstawiać. I choć nie wszystkim podoba się ta biblioteka to nie można zaprzeczyć jednemu – jQuery jest bardzo proste w nauce, jak i dalszej rozbudowie o nowe funkcje (pluginy, selektory i inne).

W tym wpisie zajmę się przydatną, aczkolwiek rzadko wykorzystywaną możliwością rozszerzania jQuery o niestandardowe selektory.

Wbudowane selektory w jQuery

Każdy kto korzysta z jQuery korzysta też z selektorów, którymi dysponuje i które są wbudowane w standardową bibliotekę.

Jako ciekawostkę warto dodać, iż selektory w jQuery są realizowane z wykorzystaniem biblioteki Sizzle. Biblioteka ta umożliwia dostęp do drzewa DOM w sposób jaki znamy z CSS.

Kilka przykładów selektorów dla przypomnienia o czym mówimy:

  • wszystkie kontenery DIV z klasą timer:
    1
    $('div.timer')
  • wszystkie elementy INPUT typu checkbox:
    1
    $(':checkbox')
  • pierwszy element LI z elementu MENU:
    1
    $('li:first', 'menu')
  • wszystkie linki poza tymi, które zawierają obrazki:
    1
    $('a:not(:has(>img))')

Jak więc widzisz, selektory ułatwiają życie i sprawiają, że tworzony kod jest re-używalny.

Niestandardowe selektory w jQuery

Twórcy biblioteki jQuery nie są w stanie przewidzieć wszystkich przypadków użycia selektorów w tworzonych aplikacjach, więc udostępnili prosty interfejs na tworzenie własnych selektorów.

Aby więc dodać niestandardowe selektory w jQuery wklepujemy następujący kod:

1
2
3
$.expr[':'].mySelector = function(objNode, intStackIndex, arrProperties, arrNodeStack) {
    // kod obsługujący selektor
};

Jest też inna opcja na rozszerzanie jQuery o własne selektory, umożliwiająca dodatkowo dodawanie więcej niż jednego selektora na raz:

1
2
3
4
5
6
7
8
9
10
11
$.extend($.expr[':'], {

    mySelector1: function(objNode, intStackIndex, arrProperties, arrNodeStack) {
        // kod obsługujący selektor
    },

    mySelector2: function(objNode, intStackIndex, arrProperties, arrNodeStack) {
        // kod obsługujący selektor
    }
   
});

Nasza funkcja z selektorem przyjmuje cztery parametry:

  1. objNode – HTML DOM Element
    Jest to referencja do bieżącego elementu w drzewie DOM. Nie jest to element jQuery, lecz węzeł DOM.
  2. intStackIndex – Integer
    Indeks aktualnego węzła w stosie, na którym zastosowano selektor (liczony od 0).
  3. arrProperties – Array
    Jest to zestaw metadanych z wywołanego selektora. Czwarty element tablicy zawiera przekazane parametry w selektorze. Pozostałe elementy nie wnoszą nic ciekawego, może poza elementem trzecim, który zawiera nawiasy zastosowane w selektorze (czasami można z tego skorzystać, co pokażę w dalszym przykładzie).
  4. arrNodeStack – Array
    Zwraca kolekcję wszystkich elementów, na których użyto selektor.
Praktyczny przykład niestandardowego selektora

Na szybko napisałem selektor, który wyszukuje zewnętrzne linki:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// define the custom selector
$.extend($.expr[':'], {

    /**
     * Check whether links are external
     *
     * @param object    HTML DOM Element
     * @param int       zero-based index of the given node
     * @param array     meta data about the custom jQuery selector execution
     * @param array     DOM elements that are being evaluated with this selector
     *
     * @return bool     link is external
    **/

    external: function(el, i, args, nodes) {

        // selector only for links
        if (el.nodeName.toLowerCase() !== 'a' && el.nodeName.toLowerCase() !== 'link') {
            return false;
        }
       
        // set default properties
        args[2] = args[2] || '"';
        args[3] = args[3] || '';
   
        // get selector arguments
        var arrArguments    = eval("([" + args[2] + args[3] + args[2] + "])"),
            arrLength       = arrArguments.length,
           
            // host has been detected
            hostDetect      = false;
       
        // search for host
        if (arrLength > 0) {
            for (var i = 0; i < arrLength; i++) {
                if (el.href.search(arrArguments[i]) != -1) {
                    hostDetect = true;
                }
            }
        } else {
            hostDetect = true;
        }
   
        // check external link
        return hostDetect && (el.rel.search('external') != -1 || el.href.search(window.location.hostname) == -1);
    }
   
});

Na szybko opiszę co robi powyższy kawałek kodu kiedy ktoś użyje selektora external na kolekcji elementów:

  • Sprawdzamy czy mamy do czynienia z odsyłaczem (element A lub LINK).
  • Ustawiamy domyślne wartości dla tablicy arrProperties – potrzebne w przypadku, gdy do selektora nie przekazano żadnych parametrów.
  • Tworzymy listę argumentów przekazanych do selektora – niestety argumenty przekazane przez jQuery są typu string, dlatego czasem trzeba kombinować, stosując np. brzydki eval do zamiany na tablicę, jak w moim przypadku.
  • Jeśli do selektora przekazano jakieś parametry to przechodzimy przez nie w pętli i kolejno sprawdzamy czy dany odsyłacz zawiera się w dozwolonych domenach (przekazanych jako argument).
  • Na koniec sprawdzamy czy link jest zewnętrzny – czy zawiera atrybut rel=”external” lub link kieruje do zewnętrznej domeny.

Selektor w praktyce wygląda następująco:

  • znajdź linki zewnętrzne i ustaw atrybut target=”_blank” (otwórz w nowy oknie):
    1
    $('a:external').attr('target', '_blank');
  • usuń wszystkie linki zewnętrzne, które prowadzą do Twittera lub Google+:
    1
    $('a:external("plus.google.com", "twitter.com")').remove();

I to by było na tyle, pozostaje zabrać się za pisanie własnych selektorów :-) Jeśli ktoś pisze i wykorzystuje we własnych aplikacjach to zapraszam do dzielenia się kodem w komentarzach.

Warto odwiedzić
  • Selektory jQuery – zanim napiszesz własny selektor sprawdź, czy nie istnieje już podobny w bibliotece jQuery.
  • Extending jQuery’s selector capabilities – świetny artykuł o tworzeniu własnych selektorów od James’a Padolsey (poparty wieloma przykładami).

Komentarze (5)

  1. Krystian 16 sierpnia 2011

    Można jQuery nie lubić, ale trzeba napisać, że właśnie dzięki takiej prostocie jest to framework #1 jeśli chodzi o .js :)

  2. Michal Wachowski 17 sierpnia 2011

    Czy aby na pewno tylko jQuery? Prototype.js też jedzie na Sizzle

    Aż sobie popiszę na blogu o własnych selektorach do prototype :) Kamila przykład w prototype wygląda tak (kasowanie elementów z rel=external i ustawionym href)

    1
    2
    3
    4
    5
    Prototype.Selector.engine.selectors.filters.external = function(elem) {
        return elem.href && elem.rel.match(/external/i);
    };

    $$('a:external[href=plus.google.com]', 'a:external[href=twitter.com]').invoke('remove');
  3. sobstel 17 sierpnia 2011

    @Krystian, dzięki takim rzeczom właśnie nie można jQuery nie lubić ;-)

  4. Kamil Brenk 17 sierpnia 2011

    @Krystian:
    Interfejs ten nie jest idealny, choćby dlatego, że w parametrach mamy niepotrzebny bałagan. Mogliby więc zrobić to lepiej (np. http://james.padolsey.com/javascript/extending-jquerys-selector-capabilities/ – UPDATE #2). Co nie zmienia faktu, że i tak jest dostatecznie prosto i przyjemnie:-)

    @Michal Wachowski:
    Nie twierdzę, że tylko jQuery jest proste. Na podstawie Twojego kodu widać, że Prototype rozwiązał sprawę dużo fajniej :-)

  5. Michal Wachowski 17 sierpnia 2011

    @Kamil – bo dał bezpośredni dostęp do Sizzle, przez co mogę modyfikować, dodawać selektory wg schematu, albo całkowicie zmienić mechanizm selectora i zrezygnować z Sizzle na coś innego.



Kamil Brenk Blog

PHP, JavaScript, SQL, HTML

  • Informacje o blogu

    Kamil Brenk

    Blog o tworzeniu aplikacji na potrzeby sieci Web.

    Praktyczne przykłady, porady i sztuczki. PHP, SQL, AJAX, JavaScript, HTML i pochodne.

    Kanał RSS

    • Najnowsze
    • Komentarze
    • Popularne
    • Liczniki w CSS
    • Wyprzedaż książek o programowaniu!
    • Niestandardowy placeholder
    • JavaScript w modułach
    • Co dalej z blogiem?
    • Interaktywna mapa w HTML i CSS
    • Olsztyn: Jak wyseparować zawartość zassaną przez file_get_content?
    • ERMLAB: Od czegoś trzeba zacząć :) Wiele osób właśnie stawia na...
    • david: co nalezy wkleić na stronę aby plik ze stylami był ladowany...
    • krynicz: Nie jestem pewien czy dobrze to rozumiem: wpisujemy OG w...
    • yaro: Jak zmienić re_write znak "_" na "-"?
    • Piotr: stworzyłem prostą stronkę w PHP, czy jest możliwość aby...
    • MichalR: Super sprawa... bardzo przydatne.. dzieki i pozdrawiam..
    • Niestandardowe czcionki na stronie
    • Sposoby wczytywania JavaScript
    • Gramatyka w PHP, część 1
    • Umowa i zaliczka dla freelancera
    • Wysyłanie wiadomości SMS w PHP
    • Projekt aplikacji po stronie klienta
    • Własny mechanizm Feed
  • Szukajka
    Wpisz co chcesz wyszukać na stronie…
  • Kategorie
    • Apache
    • Freelancer
    • Front-end Development
    • HTML5 & CSS3
    • Inne
    • JavaScript
    • Książki
    • PHP
    • Po godzinach
    • Pozycjonowanie
    • Protokół HTTP
    • SQL
    • Wyrażenia regularne
  • Moje serwisy
    • Testy zawodowe
    • Miłość, uczucia i seks
  • Czytane blogi
    • Wojciech Sznapka
    • Wojciech Soczyński
    • Michał Wachowski
    • Tomasz Kowalczyk
    • Filip Górczyński
  • Strona główna
  • Curriculum Vitae
  • O mnie
  • Przykład: Gramatyka w PHP
  • Przykład: Kompresja CSS
  • Przykład: Kompresja JavaScript
  • Przykład: Skracanie linków
  • Przykład: Wykrywanie serwera HTTP
  • Przykład: Własna bramka SMS
  • Mapa strony
  • Kontakt

Kamil Brenk © 2010. All rights reserved.

Designed by FTL Wordpress Themes brought to you by Smashing Magazine.

Do góry ∧