publikuj: Opublikuj w wykop.pl Opublikuj we flaker.pl Opublikuj na OSnews.pl Opublikuj w delicious wydrukuj
3 skomentuj »

TAGI: webhosting , jquery , biblioteka , framework , funkcja , iteracja , metoda , obiekt , parametryzacja , plugin , prototyp , wtyczka

2010-03-07 17:30:12, dodał Michał Beyer

Wzorzec projektowania wtyczek jQuery na bazie metod i funkcjiWzorzec projektowania wtyczek jQuery na bazie metod i funkcji

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.

JavaScript:
  1. /* metoda */
  2. jQuery.fn.nazwa = function() {
  3.     [...]
  4. };

JavaScript:
  1. /* funkcja */
  2. jQuery.nazwa = function() {
  3.     [...]
  4. };

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.

JavaScript:
  1. /* wywołanie: metoda */
  2. $(element).nazwa();

JavaScript:
  1. /* wywołanie: funkcja */
  2. var x = $.nazwa();
  3. 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.

JavaScript:
  1. /* metoda z parametrem */
  2. jQuery.fn.nazwa = function(options) {
  3.     [...]
  4. };

JavaScript:
  1. /* funkcja z parametrem */
  2. jQuery.nazwa = function(options) {
  3.     [...]
  4. };

Umowne zasady tworzenia pluginów

Twórcy jQuery, poprzez dokumentację, podpowiadają jakimi zasadami należy się kierować, aby poprawnie tworzyć wtyczki. Mianowicie:

  1. 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.
  2. Nazwy plików powinny być formowane następująco: jquery.[nazwa_wtyczki].js. Przykładowo: jquery.nazwa.js.
  3. W metodach this jest referencją do aktualnie przetwarzanego obiektu jQuery.
  4. 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życiem jQuery.noConflict().
  5. 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 (;).
  6. Użycie .each pozwala na bezproblemową iteracje po wszystkich elementach na których operujemy w metodzie.
  7. 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:

HTML:
  1. <ol>
  2.     <li>play</li>
  3.     <li>the</li>
  4.     <li>game</li>
  5. </ol>

JavaScript:
  1. $('ol').each(function(i, e) {
  2.     // e == this
  3.     // e jest warte użycia w konkretnych zagnieżdżeniach, kiedy to this nie zawsze wskazuje na aktualnie analizowany element pętli
  4.     $(e).html('<strong>' + $(this).text() + '</strong>');
  5. });

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.

JavaScript:
  1. jQuery.fn.filtrate = function() {
  2.     var defaults = {           
  3.         length:     6, // minimalna długość wprowadzanego tekstu
  4.         regexp:     /[0-9a-z]/gi, // kryterium wprowadzanych znaków
  5.         returnto:   'footer' // informacja o błędzie zostanie zwrócona do tego elementu
  6.     };
  7.  
  8.     alert(defaults.regexp); // przykładowe użycie
  9. };

Za pomocą parametru options przekażemy spersonalizowane dane konfiguracyjne.

JavaScript:
  1. jQuery.fn.filtrate = function(options) {
  2.     var defaults = {           
  3.             length:     6, // minimalna długość wprowadzanego tekstu
  4.             regexp:     /[0-9a-z]/gi, // kryterium wprowadzanych znaków
  5.             returnto:   'footer' // informacja o błędzie zostanie zwrócona do tego elementu
  6.     };
  7.    
  8.     // rozszerzenie domyślnej konfiguracji
  9.     var options = $.extend(defaults, options);
  10.    
  11.     alert(options.regexp); // przykładowe użycie
  12. };

Propozycje wywołań metody z użyciem argumentów.

JavaScript:
  1. $('#nick').filtrate({length: 3});
  2. // lub
  3. $('#nick').filtrate({
  4.     regexp: /[0-9]/gi,
  5.     length: 3
  6. });

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.

JavaScript:
  1. jQuery.fn.filtrate = function(options) {   
  2.     // rozszerzenie domyślnej konfiguracji bez jej nadpisywania (pierwszy argument pusty)
  3.     var options = $.extend({}, $.fn.filtrate.defaults, options);
  4.    
  5.     alert(options.regexp); // przykładowe użycie
  6. };
  7.  
  8. $.fn.filtrate.defaults = {
  9.     length:     6, // minimalna długość wprowadzanego tekstu
  10.     regexp:     /[0-9a-z]/gi, // kryterium wprowadzanych znaków
  11.     returnto:   'footer' // informacja o błędzie zostanie zwrócona do tego elementu
  12. };

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() [...]);.

JavaScript:
  1. // nadpisanie długości spoza parametru
  2. $.fn.filtrate.defaults.length = 3;
  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.

JavaScript:
  1. (function($) { // start: closure
  2.     $.fn.filtrate = function() {   
  3.         $.logs(1); // ostrzeżenie z zawartością '1'
  4.        
  5.         logs(1); // ostrzeżenie z zawartością '1'
  6.     };     
  7.    
  8.     function logs($obj) {
  9.         alert($obj);
  10.     };
  11.    
  12.     $.logs = function($obj) {
  13.         alert($obj);
  14.     };
  15. })(jQuery); // end: closure
  16.  
  17. jQuery(document).ready(function() {
  18.     $('#nick').filtrate();
  19.    
  20.     $.logs(1); // ostrzeżenie z zawartością '1'
  21.     logs(1); // brak dostępu do tej funkcji z zewnątrz
  22. });

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.

HTML:
  1. [...]
  2.         <footer>
  3.             <p>Wpisz swój script nick</p>
  4.             <p>Wpisz swój a href nick</p>
  5.         </footer>
  6. [...]

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.

JavaScript:
  1. jQuery.fn.filtrate = function(options) {   
  2.     var defaults = {
  3.         erase:      /a href/g, // tekst do usunięcia
  4.         usemethod:  true // pomocnicze, użycie metody lub funkcji
  5.     };
  6.     // rozszerzenie domyślnej konfiguracji
  7.     var options = $.extend(defaults, options);
  8.    
  9.     return this.each(function() {
  10.         var context = $(this).html(); // pobranie zawartości elementu
  11.         switch (options.usemethod) // dodany w celach naukowych ;]
  12.         {
  13.             case false:
  14.                 context = $.cleanup(context, options.erase); // wywołanie funkcji
  15.                 break;
  16.             default:
  17.                 context = $.fn.filtrate.cleanup(context, options.erase); // wywołanie metody
  18.                 break;
  19.         }
  20.         $(this).html(context); // zapisanie zmodyfikowanej zawartości elementu
  21.     });
  22. };     
  23.  
  24. // metoda zaimplementowana we wtyczce
  25. jQuery.fn.filtrate.cleanup = function(context, erase) {
  26.     return context.replace(erase, ''); // usunięcie frazy z ciągu
  27. };
  28.  
  29. // osobna funkcja
  30. jQuery.cleanup = function(context, erase) {
  31.     return context.replace(erase, ''); // usunięcie frazy z ciągu
  32. };
  33.  
  34. jQuery(document).ready(function() {
  35.     // tryb wywoływania z metody
  36.     $('footer p').filtrate({erase: /script/g});
  37.    
  38.     // tryb wywoływania spoza metody
  39.     return $('footer p').each(function() {
  40.         var context = $(this).html();
  41.         context = $.fn.filtrate.cleanup(context, /a href/g);
  42.         context = $.cleanup(context, /script/g);
  43.         $(this).html(context);
  44.     });
  45. });

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.

HTML:
  1. [...]
  2.         <header>
  3.             <p><input type="text" id="nick"></p>
  4.             <p><input type="text" id="nick_second"></p>
  5.         </header>
  6. [...]

Wywołanie odbywać się będzie w momencie zwolnienia przycisku klawiatury.

JavaScript:
  1. jQuery(document).ready(function() {
  2.     $('input').keyup(function() {
  3.         $('input').filtrate({regexp: /[a-z]/g});
  4.     });
  5. });

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.

JavaScript:
  1. // metoda testująca poprawność wpisanych znaków
  2.     $.fn.filtrate.preg_match = function(pattern, subject) {
  3.         var subject_splited = subject.split(''); // utworzenie tablicy znaków
  4.         for ( i=0; i < subject_splited.length; ++i ) // iteracja po każdym ze znaków
  5.         {
  6.             // jeżeli którykolwiek znak nie będzie odpowiadał wzorcowi metoda zwraca fałsz
  7.             if ( subject_splited[i].match(new RegExp(pattern)) == null )
  8.             {
  9.                 return false;
  10.             }
  11.         }
  12.         return true; // wszystkie znaki są odpowiednie, prawda - jedziemy dalej ;]
  13.     };
  14.    
  15.     // metoda porównująca długości ciągów
  16.     $.fn.filtrate.strlen_compare = function(string, min_length) {
  17.         var length = string.length;
  18.         if ( length < 1 ) // tekst nie został wpisany
  19.         {
  20.             return false;   
  21.         }
  22.         else if ( length < min_length ) // tekst krótszej długości niż wymagana
  23.         {
  24.             return min_length - length;
  25.         }
  26.         return true; // tekst odpowiedniej długości
  27.     };
  28.    
  29.     // funkcja prywatna, logująca akcje
  30.     function log_alerts(returnto, text)
  31.     {
  32.         // jeżeli nie podano tekstu do wypisania, czyścimy zawartość znacznika przetrzymującego ostrzeżenia i informacje
  33.         if ( text == undefined )
  34.         {
  35.             $(returnto).html('');
  36.             return;
  37.         }
  38.        
  39.         // dodanie nowego akapitu z zwróconą treścią do podanego znacznika
  40.         $('<p>' + text + '</p>').appendTo(returnto);
  41.     }

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.

JavaScript:
  1. (function($) { // start: closure
  2.     $.fn.filtrate = function(options) {
  3.         // rozszerzenie domyslnej konfiguracji bez jej nadpisywania (pierwszy argument pusty)
  4.         var options = $.extend({}, $.fn.filtrate.defaults, options);
  5.    
  6.         var context, strlen_compared_result;
  7.        
  8.         // czyszczenie znacznika zawierającego informacje o stanie pól
  9.         log_alerts(options.returnto);
  10.        
  11.         // sprawdzenie każdego elementu
  12.         return this.each(function(i) {
  13.             context = $(this);
  14.            
  15.             // testowanie długości
  16.             strlen_compared_result = $.fn.filtrate.strlen_compare(context.val(), options.length);
  17.             switch (strlen_compared_result)
  18.             {
  19.                 case false: // w wypadku niewpisania tekstu
  20.                     // logowanie akcji
  21.                     log_alerts(options.returnto, 'Pole <strong>' + context.attr('id') + '</strong> jest puste');
  22.                     return true;
  23.                     break;
  24.                 case true: // w wypadku odpowiedniej długości tekstu
  25.                     // puste
  26.                     break;
  27.                 default: // w wypadku za krótkiego tekstu zwracany jest: tekst + wymagana długość + ilość brakujących znaków
  28.                     log_alerts(options.returnto, 'Pole <strong>' + context.attr('id') + '</strong> zawiera za mało znaków (minimum: ' + options.length + ' / brakuje: ' + strlen_compared_result + ')');
  29.                     return true;
  30.                     break;
  31.             }
  32.            
  33.             // testowanie typów znaków znajdujących się w tekście
  34.             switch ($.fn.filtrate.preg_match(options.regexp, context.val()))
  35.             {
  36.                 case false: // w wypadku pojawienia się niedozwolonych znaków
  37.                     log_alerts(options.returnto, 'Pole <strong>' + context.attr('id') + '</strong> zawiera niedozwolone znaki');
  38.                     return true;
  39.                     break;
  40.                 default: // w wypadku poprawnego sformowania tekstu
  41.                     log_alerts(options.returnto, 'Pole <strong>' + context.attr('id') + '</strong> jest prawidłowo wypełnione');
  42.                     break;
  43.             }
  44.         });
  45.     };
  46.  
  47.     $.fn.filtrate.defaults = {
  48.         length:  6, // minimalna dlugosc wprowadzanego tekstu
  49.         regexp:  /[0-9a-z]/gi, // kryterium wprowadzanych znaków
  50.         returnto:   'footer' // informacja o bledzie zostanie zwrócona do tego elementu
  51.     };
  52.  
  53.     // pozostałe dwie metody + prywatna funkcja
  54. })(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.

Strona tego bloga

publikuj: Opublikuj w wykop.pl Opublikuj we flaker.pl Opublikuj na OSnews.pl Opublikuj w delicious wydrukuj
3 skomentuj »

Polecamy

Reklama

Komentarze

  • Anonimosław

    #1 Anonimosław 2010-03-08 11:36:08 0

    Na początku podane, że trzeba korzystać w pluginach z jQuery, a nie z $, a potem wewnarz metod i funkicji $ jest uzywany.

    Artykul calkiem dobry. Trzeba troche pomyslec co sie czyta :)

    IP: 94.68.146.[...] Opera/9.80 (Windows NT 5.0; U; en) Presto/2.5.22 Version/10.50

  • m1chu

    #2 m1chu® 2010-03-08 12:19:46 0

    Nie trzeba, a powinno się :] Więcej o tym, także tutaj: http://m1chu.eu/2008/10/02/kolizje-wspoldzialania-bibliotek-javascript-na-podstawie-wtyczek-wordpress/

    Po za tym, wystarczy, że blok zamykający/zawierający będzie oparty na jQuery. Wewnątrz można korzystać już z $.

    http://m1chu.eu/2010/03/06/wzorzec-projektowania-wtyczek-jquery-na-bazie-metod-i-funkcji/ - zapraszam do subskrypcji bloga :]

    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)

  • fastred

    #3 fastred 2010-03-09 11:13:15 0

    Tytuł jest nieco mylący, za bardzo kojarzy się z wzorcami projektowymi(GoF).

    Wydaje mi się, że powinien brzmieć raczej: "Przykład projektowania wtyczek do jQuery"

    Powodzenia przy pisaniu kolejnych tekstów :)

    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

Uwaga! Możesz zarejestrować się w serwisie i w ten sposób zarezerwować swój nick oraz ominąć konieczność ciągłego odczytywania wyrazów.

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.

Polecane książki

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ł