Wzorzec projektowania wtyczek jQuery na bazie metod i funkcji

Praca z jQuery prędzej, czy później wymusza na nas kompleksowe tworzenie rozwiązań wykonywanych po stronie użytkownika. Metody filtrujące, animacje, czy efekty na galeriach zdjęć łatwiej będzie osiągnąć i rozwijać posługując się, łączącymi się na wtyczki, mechanizmami metod i funkcji, dostępnymi w frameworku.
Metoda, a funkcja?
Podstawowa różnica pomiędzy nimi polega na tym, że operują na innych obiektach. Każda nowa metoda musi być dołączona do prototypu jQuery.fn, a funkcja po prostu do obiektu jQuery.
- /* metoda */
- jQuery.fn.nazwa = function() {
- [...]
- };
- /* funkcja */
- jQuery.nazwa = function() {
- [...]
- };
W praktyce powoduje to, że do metody odwołujemy się operując na jakimś elemencie, identyfikatorze, bądź na klasie znajdującej się na stronie internetowej, a w przypadku funkcji, po prostu ją wywołujemy w celu wykonania jakieś akcji.
- /* wywołanie: metoda */
- $(element).nazwa();
- /* wywołanie: funkcja */
- var x = $.nazwa();
- alert(x);
Obydwie możemy parametryzować. Argumentem może być zwykła zmienna, dowolnego, obsługiwanego przez JavaScript typu lub obiekt przechowujący zbiór pól o konkretnym przeznaczeniu.
- /* metoda z parametrem */
- jQuery.fn.nazwa = function(options) {
- [...]
- };
- /* funkcja z parametrem */
- jQuery.nazwa = function(options) {
- [...]
- };
Umowne zasady tworzenia pluginów
Twórcy jQuery, poprzez dokumentację, podpowiadają jakimi zasadami należy się kierować, aby poprawnie tworzyć wtyczki. Mianowicie:
- Terminologie funkcja, metoda, czy klasa są stosowanie umownie, z racji specyfiki przeznaczenia każdego z tych elementów. Fraza metoda jest w powyższym wypadku synonimem całego pojęcia wtyczki.
- Nazwy plików powinny być formowane następująco: jquery.[nazwa_wtyczki].js. Przykładowo: jquery.nazwa.js.
- W metodach
thisjest referencją do aktualnie przetwarzanego obiektu jQuery. - Powinno się dołączać wtyczkę do obiektu nadrzędnego poprzez
jQuery, a nie$, co użytkownikom zwiększa pole manewru we wprowadzaniu zmian z użyciemjQuery.noConflict(). - W celu uniknięcia problemów z poprawnością kodu po jego wcześniejszej kompresji, wszelkie metody oraz funkcje powinny zakańczać się znakiem średnika (
;). - Użycie
.eachpozwala na bezproblemową iteracje po wszystkich elementach na których operujemy w metodzie. - Metoda domyślnie musi zwracać wartość.
.each(function(indeks, element)) - przenosi się po każdym elemencie drzewa DOM, będących częścią obiektu jQuery, za każdym razem wykonując funkcję określoną jako parametr opisywanej metody.
Przykład:
- $('ol').each(function(i, e) {
- // e == this
- // e jest warte użycia w konkretnych zagnieżdżeniach, kiedy to this nie zawsze wskazuje na aktualnie analizowany element pętli
- $(e).html('<strong>' + $(this).text() + '</strong>');
- });
Przekazywanie parametrów w celu kontrolowania zachowań wtyczki
Czym byłby plugin bez możliwości prostej parametryzacji określonych zachowań? W celu ułatwienia zrozumienia ich tworzenia postaramy się stworzyć bardzo prosty tester poprawności wpisywanych do pola danych (np. o identyfikatorze #nick). Sprawdzimy w nich, czy wprowadzony tekst będzie odpowiadał odpowiedniemu wzorcowi wyrażenia regularnego i będzie wystarczająco długi. Ponadto określimy do jakiego elementu zwrócić informację na wypadek niespełnienia wcześniej wymienionych kryteriów. Warto więc będzie zdefiniować na stałe wartości tych opcji oraz dać przyszłym jej użytkownikom możliwość nadpisania ich w argumentach wywoływanej metody.
jQuery.extend([ tryb_rekursywny ,] obiekt_rozszerzany, obiekt_1_rozszerzający [, ... obiekt_n_rozszerzający]) - łączenie dwóch lub kilku właściwości obiektów do osobnego.
- tryb_rekursywny - przyjmuje wartości logiczne (true/false) w celu wykonania rekursywnego łączenia,
- obiekt_rozszerzany - element nadpisywany (w celu uniknięcia należy pozostawić puste),
- obiekt_x_rozszerzający - elementy łączone.
Załóżmy więc, że nazwiemy naszą wtyczkę filtrate, a domyślne dane będziemy przetrzymywać w obiekcie defaults.
- jQuery.fn.filtrate = function() {
- var defaults = {
- length: 6, // minimalna długość wprowadzanego tekstu
- regexp: /[0-9a-z]/gi, // kryterium wprowadzanych znaków
- returnto: 'footer' // informacja o błędzie zostanie zwrócona do tego elementu
- };
- alert(defaults.regexp); // przykładowe użycie
- };
Za pomocą parametru options przekażemy spersonalizowane dane konfiguracyjne.
- jQuery.fn.filtrate = function(options) {
- var defaults = {
- length: 6, // minimalna długość wprowadzanego tekstu
- regexp: /[0-9a-z]/gi, // kryterium wprowadzanych znaków
- returnto: 'footer' // informacja o błędzie zostanie zwrócona do tego elementu
- };
- // rozszerzenie domyślnej konfiguracji
- var options = $.extend(defaults, options);
- alert(options.regexp); // przykładowe użycie
- };
Propozycje wywołań metody z użyciem argumentów.
- $('#nick').filtrate({length: 3});
- // lub
- $('#nick').filtrate({
- regexp: /[0-9]/gi,
- length: 3
- });
Do ustawień nie ma jednak publicznego dostępu. Możemy co prawda skonfigurować je przy wywoływaniu wtyczki, ale nie bezpośrednio, np. później. W tym celu należy przenieść domyślne wartości do własności składowej metody oraz połączyć wprowadzane i predefiniowane dane bez modyfikacji tych drugich.
- jQuery.fn.filtrate = function(options) {
- // rozszerzenie domyślnej konfiguracji bez jej nadpisywania (pierwszy argument pusty)
- var options = $.extend({}, $.fn.filtrate.defaults, options);
- alert(options.regexp); // przykładowe użycie
- };
- $.fn.filtrate.defaults = {
- length: 6, // minimalna długość wprowadzanego tekstu
- regexp: /[0-9a-z]/gi, // kryterium wprowadzanych znaków
- returnto: 'footer' // informacja o błędzie zostanie zwrócona do tego elementu
- };
Zewnętrzny programista ma teraz szansę wykonania ustawień przed i po wywołaniu wtyczki, a także bez konieczności robienia tego w bloku jQuery(document).ready(function() [...]);.
- // nadpisanie długości spoza parametru
- $.fn.filtrate.defaults.length = 3;
- $('#nick').filtrate({regexp: /[0-9]/gi});
Prywatne funkcje - ukrywamy poszczególne elementy
Nie każda partia kodu powinna być dostępna do bezpośredniego wywołania przez użytkownika wtyczki. Przykładem może być tutaj funkcja użytkowa, stworzona stricte do celów danej metody. Aby tego dokonać, należy opleść całą definicję pluginu w funkcję zamykającą (closure) oraz po za samą metodą użyć standardowej formy tworzenia funkcji w JavaScript. Metodologia tworzenia funkcji w jQuery nie zda tutaj prawidłowego rezultatu. Dostęp będzie nadal publiczny.
- (function($) { // start: closure
- $.fn.filtrate = function() {
- $.logs(1); // ostrzeżenie z zawartością '1'
- logs(1); // ostrzeżenie z zawartością '1'
- };
- function logs($obj) {
- alert($obj);
- };
- $.logs = function($obj) {
- alert($obj);
- };
- })(jQuery); // end: closure
- jQuery(document).ready(function() {
- $('#nick').filtrate();
- $.logs(1); // ostrzeżenie z zawartością '1'
- logs(1); // brak dostępu do tej funkcji z zewnątrz
- });
Wywoływanie funkcji oraz innych metod we wtyczce
Zaistnieć może także sytuacja przeciwna do powyższej. Z różnych powodów możemy chcieć skorzystać z publicznie dostępnej metody, zaimplementowanej wewnątrz wtyczki, którą tworzymy lub z funkcji przedstawionych na początku artykułu.
Mamy przykładowy zestaw paragrafów, z których będziemy chcieli wykluczyć pewny ciąg znaków. Ponieważ będziemy sprawdzać więcej elementów niż jeden, skorzystamy z wcześniej wymienionej metody iteracyjnej .each(), a następnie pobierzemy ich zawartości, usuniemy stosowną frazę (korzystając z osobnej funkcji/metody) i zwrócimy wynik do znacznika je zawierającego.
- jQuery.fn.filtrate = function(options) {
- var defaults = {
- erase: /a href/g, // tekst do usunięcia
- usemethod: true // pomocnicze, użycie metody lub funkcji
- };
- // rozszerzenie domyślnej konfiguracji
- var options = $.extend(defaults, options);
- return this.each(function() {
- var context = $(this).html(); // pobranie zawartości elementu
- switch (options.usemethod) // dodany w celach naukowych ;]
- {
- case false:
- context = $.cleanup(context, options.erase); // wywołanie funkcji
- break;
- default:
- context = $.fn.filtrate.cleanup(context, options.erase); // wywołanie metody
- break;
- }
- $(this).html(context); // zapisanie zmodyfikowanej zawartości elementu
- });
- };
- // metoda zaimplementowana we wtyczce
- jQuery.fn.filtrate.cleanup = function(context, erase) {
- return context.replace(erase, ''); // usunięcie frazy z ciągu
- };
- // osobna funkcja
- jQuery.cleanup = function(context, erase) {
- return context.replace(erase, ''); // usunięcie frazy z ciągu
- };
- jQuery(document).ready(function() {
- // tryb wywoływania z metody
- $('footer p').filtrate({erase: /script/g});
- // tryb wywoływania spoza metody
- return $('footer p').each(function() {
- var context = $(this).html();
- context = $.fn.filtrate.cleanup(context, /a href/g);
- context = $.cleanup(context, /script/g);
- $(this).html(context);
- });
- });
Osiągamy postawiony cel
Na podstawie powyższych rad możemy osiągnąć postawiony sobie cel - tester poprawności wpisywanych danych do pola typu input. Rozwiązanie będzie zezwalać na testowanie wielu pól naraz.
Wywołanie odbywać się będzie w momencie zwolnienia przycisku klawiatury.
- jQuery(document).ready(function() {
- $('input').keyup(function() {
- $('input').filtrate({regexp: /[a-z]/g});
- });
- });
W przestrzeni funkcji zawierającej wtyczkę znajdzie się prywatna funkcja logująca akcje oraz dwie publiczne metody implementowane w pluginie. Pierwsza z nich będzie sprawdzać poprawność wpisywanego tekstu pod względem zadanego wzorca, a druga długość wprowadzonego ciągu znaków.
- // metoda testująca poprawność wpisanych znaków
- $.fn.filtrate.preg_match = function(pattern, subject) {
- var subject_splited = subject.split(''); // utworzenie tablicy znaków
- for ( i=0; i < subject_splited.length; ++i ) // iteracja po każdym ze znaków
- {
- // jeżeli którykolwiek znak nie będzie odpowiadał wzorcowi metoda zwraca fałsz
- if ( subject_splited[i].match(new RegExp(pattern)) == null )
- {
- return false;
- }
- }
- return true; // wszystkie znaki są odpowiednie, prawda - jedziemy dalej ;]
- };
- // metoda porównująca długości ciągów
- $.fn.filtrate.strlen_compare = function(string, min_length) {
- var length = string.length;
- if ( length < 1 ) // tekst nie został wpisany
- {
- return false;
- }
- else if ( length < min_length ) // tekst krótszej długości niż wymagana
- {
- return min_length - length;
- }
- return true; // tekst odpowiedniej długości
- };
- // funkcja prywatna, logująca akcje
- function log_alerts(returnto, text)
- {
- // jeżeli nie podano tekstu do wypisania, czyścimy zawartość znacznika przetrzymującego ostrzeżenia i informacje
- if ( text == undefined )
- {
- $(returnto).html('');
- return;
- }
- // dodanie nowego akapitu z zwróconą treścią do podanego znacznika
- $('<p>' + text + '</p>').appendTo(returnto);
- }
Domyślne opcje ustawimy poza wtyczką. W niej samej, dla każdego wskazanego elementu strony, sprawdzimy najpierw warunek długości, a gdy zostanie spełniony, także zawartości.
- (function($) { // start: closure
- $.fn.filtrate = function(options) {
- // rozszerzenie domyslnej konfiguracji bez jej nadpisywania (pierwszy argument pusty)
- var options = $.extend({}, $.fn.filtrate.defaults, options);
- var context, strlen_compared_result;
- // czyszczenie znacznika zawierającego informacje o stanie pól
- log_alerts(options.returnto);
- // sprawdzenie każdego elementu
- return this.each(function(i) {
- context = $(this);
- // testowanie długości
- strlen_compared_result = $.fn.filtrate.strlen_compare(context.val(), options.length);
- switch (strlen_compared_result)
- {
- case false: // w wypadku niewpisania tekstu
- // logowanie akcji
- log_alerts(options.returnto, 'Pole <strong>' + context.attr('id') + '</strong> jest puste');
- return true;
- break;
- case true: // w wypadku odpowiedniej długości tekstu
- // puste
- break;
- default: // w wypadku za krótkiego tekstu zwracany jest: tekst + wymagana długość + ilość brakujących znaków
- log_alerts(options.returnto, 'Pole <strong>' + context.attr('id') + '</strong> zawiera za mało znaków (minimum: ' + options.length + ' / brakuje: ' + strlen_compared_result + ')');
- return true;
- break;
- }
- // testowanie typów znaków znajdujących się w tekście
- switch ($.fn.filtrate.preg_match(options.regexp, context.val()))
- {
- case false: // w wypadku pojawienia się niedozwolonych znaków
- log_alerts(options.returnto, 'Pole <strong>' + context.attr('id') + '</strong> zawiera niedozwolone znaki');
- return true;
- break;
- default: // w wypadku poprawnego sformowania tekstu
- log_alerts(options.returnto, 'Pole <strong>' + context.attr('id') + '</strong> jest prawidłowo wypełnione');
- break;
- }
- });
- };
- $.fn.filtrate.defaults = {
- length: 6, // minimalna dlugosc wprowadzanego tekstu
- regexp: /[0-9a-z]/gi, // kryterium wprowadzanych znaków
- returnto: 'footer' // informacja o bledzie zostanie zwrócona do tego elementu
- };
- // pozostałe dwie metody + prywatna funkcja
- })(jQuery); // end: closure
Przykład ten powinien przybliżyć kwestię wcześniej poruszone w artykule. Na jego podstawie udostępniłem także testową stronę oraz gotowe pliki do pobrania.
Polecamy
Reklama
Komentarze
Aby dodać komentarz, musisz podać swój nick, treść komentarza oraz poprawnie przepisać oba słowa z obrazka
(słowa muszą być rozdzielone spacją).
W treści komentarza można używać języka formatowania BBcode.
Popularne
Pobierałeś pirackie pliki? Uważaj! Kontrole antypirackie w domach użytkowników to codzienność
36
Pobieraczek.pl pozwie internautów, którzy nie chcą płacić abonamentu
1455
Debata w sprawie ACTA: internauci spodziewali się chyba czegoś innego
14
Wynalazca WWW przed sądem: walczy tam o wolny dostęp do webowych technologii dla każdego
8
Programowanie w środowisku Android – wprowadzenie do projektowania aplikacji dla urządzeń mobilnych
15
Internet w EU bez Facebooka i Google? Firmy nie mają wyboru: albo się dostosują, albo…
10
Zaktualizuj PHP do wersji 5.4, zyskasz za darmo nawet o kilkudziesiąt procent wyższą wydajność aplikacji
6
MSWiA zamówiło narzędzia do „złamania” Tora i podsłuchiwania internautów. Czy złamało przy tym prawo?
89
[Aktualizacja] Facebook zablokował Demotywatory.pl. W czym zawiniły?
36
FBI zamknęło Megaupload. Anonimowi dali się sprowokować. Teraz ich akcja uzasadni potrzebę SOPA?
17
Pobieraczek.pl pozwie internautów, którzy nie chcą płacić abonamentu
1455
Programowanie w środowisku Android – wprowadzenie do projektowania aplikacji dla urządzeń mobilnych
15
„Donald matole, twój rząd dopadną kibole” – hakerska elita przyłącza się do walki z ACTA
23
Pobierałeś pirackie pliki? Uważaj! Kontrole antypirackie w domach użytkowników to codzienność
36
Społeczność
WebDev @slawek22
OK. Rozumiem i wiem, że koncerny stosują regionalizacje i nie...
slawek22 >są inne rodzime serwisy VOD z legalny filmami
WebDev no fakt... jeśli...
WebDev @BLACK BEAR®
"bo denerwujesz mi ludzi na forum” lool
Minister Zdrowia...
tobas ClickShop bardzo wolno działa!!!
Nie dajcie się nabrać na wygląd i...
blackbear @WebDev, widzę że muszę Ci rozjaśnić we łbie. Czytaj więc i nie pisz...
Jay Brak obsługi aplikacji x86/x64 ze zwykłych desktopów to kręcenie na siebie...
WebDev @slawek22
Tak jak ze wszystkim tak i z prawem własności można przesadzić...
- gardius: Dobra hurtownia sportowa (1)
- gardius: Tanie książki gdzie warto kupować? (1)
- Najdmen.pl: PROMOCJA, 500 DOMEN .EU ZA 1 PLN NETTO ! (1)
- VMLine: [Oferta] Serwery VPS Xen-HVM/OpenVZ z darmową administracją (2)
- Marek: Generowanie PDFa (2)
- Marek: problem z menu (2)
- Marek: Własne checkboxy w HTML,CSS (1)
Polecane książki
Praca
Czytaj Webhosting
Chcesz być na bieżąco z naszymi informacjami? Zapisz się na Newsletter.
Zarejestruj domenę
Sprawdź dostępność swojej domeny:
| .pl: | 0 zł | .com: | 19.90 zł | |
|---|---|---|---|---|
| .com.pl: | 0 zł | .eu: | 19.90 zł |









#1 Anonimosław 2010-03-08 11:36:08 0
IP: 94.68.146.[...] Opera/9.80 (Windows NT 5.0; U; en) Presto/2.5.22 Version/10.50
#2 m1chu® 2010-03-08 12:19:46 0
IP: 83.23.0.[...] Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 (.NET CLR 3.5.30729)
#3 fastred 2010-03-09 11:13:15 0
IP: 83.7.41.[...] Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; pl; rv:1.9.2) Gecko/20100115 Firefox/3.6