Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Jak zrozumieć wtyczki jQuery?
Forum PHP.pl > Forum > Po stronie przeglądarki > JavaScript
Adi32
Witajcie,

Robię różne wtyczki JS między innymi za pomocą takich artykułów:
http://www.altcontroldelete.pl/artykuly/ja...yczki-w-jquery/
ale te moje wtyczki często potrzebują działać niestandardowo a ja nie moge nic zrobić bo nie rozumiem jak to działa...
Szukam po necie jakichś wytłumaczeń ale nic konkretnego nie działa...

Nie widzę w tym żadnej logiki i nawet jak działa to nie wiem dlaczego.

Dziwność zaczyna się w momencie
...return this.each(function() {...

Co do wieloinstancyjności i kłopotach z tym związanych spędziłem dziesiątki godzin...

Znacie może jakiś dobry artykuł na ten tenat w języku polskim? A może ktoś potrafi sam wyjaśnić jak to jest z tymi wtyczkami jQuery....
iwosz
Cześć,

generalnie według mnie najpierw powinno się nauczyć pisać w jQuery a potem brać za tworzenie pluginów smile.gif wydaje mi się że Ty zacząłeś od tego drugiego i dlatego masz problemy ze zrozumieniem kodu zamieszczonego pod linkiem który podałeś.
Mogę Ci to w skrócie wytłumaczyć ale to Ty sam musisz przysiąść i poćwiczyć korzystanie z jQuery, zachęcam do tego bo warto! wink.gif

Co do tego kodu, przede wszystkim składnia jQuery opiera się na podstawowym obiekcie o takiej samej nazwie, zapisywany skrótowo jako "$". Natomiast każdy plugin to tak na prawdę definicja nowej funkcji która albo rozszerza już istniejące mechanizmy w jQuery ($.extend) albo wprowadza zupełnie nowe. Dlatego definicja tego pluginu zaczyna się od "(function($)", a kończy się zwróceniem obiektu jQuery czyli "(jQuery);".

Kawałek kodu który wymieniłeś
  1. return this.each(function() {...}

zwraca wykonanie funkcji jQuery.each(), w tym przypadku jest to samo co:
  1. this.each(function() {
  2. var oElement = $(this);
  3. oElement.data('myplugin', {
  4. pluginoptions: oOptions
  5. });
  6. // init operations....
  7. initExampleHelperFunction();
  8. if(null != oElement.data('myplugin')
  9. .pluginoptions.ExtraInitFunction){
  10. oElement.data('myplugin').pluginoptions.ExtraInitFunction();
  11. }
  12. });
  13. return;

Zobacz tutaj po polski opis: http://www.i-mateusz.com/news/jak-napisac-...gin-jquery.html polecam też dokumentację jQuery: http://api.jquery.com/

Cytat("Adi32")
Co do wieloinstancyjności i kłopotach z tym związanych spędziłem dziesiątki godzin...
to nie wiem czy dobrze rozumiem, chciałbyś wdrożyć wzorzec Singleton w JS? Może coś takiego:

http://www.dofactory.com/javascript-singleton-pattern.aspx
trueblue
Cytat(Adi32 @ 13.03.2014, 09:44:19 ) *
Dziwność zaczyna się w momencie
...return this.each(function() {...

Jeśli twoja wtyczka nazywa się superWtyczka i wywołasz ją tak:
  1. $(selektor).superWtyczka

to wewnątrz powyższej funkcji, w zmiennej this otrzymasz kolejne elementy, które wybrał selektor.
Adi32
Dzięki za odpowiedź Panowie.

Napisałem już dość sporo działających wtyczek i innych rzeczy pomocy jQuery także zdaje mi się, że podstawy już znam.

Żeby lepiej pokazać o co mi chodzi napisze co robiłem ostatnio:
Napisałem wtyczkę do jQuery (kod mam w pracy, jutro wrzucę) która nazywa się Validate.

Potrzebowałem aby konkretne pola konkretnych formularzy były walidowane tylko w konkretnych przypadkach dynamicznie.

Ostatecznie działa to tak, że powiedzmy mamy formularz (#loginForm) na stronie z dwoma polami - email, hasło:

  1. oValidate = new Validate('loginForm')
  2. oValidate.add('inputEmail', 'email') // pierwszy parametr to nazwa pola, drugi to typ walidacji, jeśli nie podamy typu walidacji to załaduje się domyślny noEmpty


W tym momencie jeśli spróbujemy dać submit albo blur na inpucie klasa Validate odpala domyślny komunikat przy polu o błędnych danych, dla każdego pola możemy też przypisać konkretny komunikat.

  1. oValidate.remove('inputEmail') // a ta linia powoduje, że pole nie jest już walidowane, sensu niby nie ma ale to tylko przykład. W procesie zamówienia który tworze takie rozwiązanie jest niezbędne.


Problemy zaczęły pojawiać się, gdy na stronie pojawiały się 2 formularze. Mają działać pod klasą Validate bez kolidacji, to właśnie miałem na myśli mówiąc o wielo-instancyjności, czyli raczej odwrotności singletona. Problem wyglądał tak że nie dawało się kliknąć w submit jeśli w którymkolwiek formularzu coś się nie walidowało.

Z tym też sobie poradziłem, ale raczej metodą prób i błędów tworząc kod którego nie rozumiem dlatego postanowiłem założyć ten temat. Wciąż nie rozumiem return this.each... each to jak mniemam pętla po wszystkich "obiektach obiektu" (tak mi się przynajmniej wydaje) także jaki jest sens takiego zapisu? Wierzę , że jak to zrozumiem to zrozumiem i resztę.

Jak mówiłem, wrzucam kod:

  1. (function($) {
  2. var m_oDefaults = {
  3. nTestSetting: 0,
  4. sTestSetting: 'test',
  5. ExtraInitFunction: null,
  6. formName: null
  7. };
  8.  
  9. var initExampleHelperFunction = function(oElement){
  10. self = m_aoFunctions;
  11.  
  12. oElement.find('input').on('blur', function(){
  13. return self.blur($(this).attr('name'), oElement);
  14. });
  15. oElement.find('input[type="submit"]').on('click', function(){
  16. return self.checkAll(oElement);
  17. });
  18.  
  19. };
  20.  
  21. var m_aoFunctions = {
  22.  
  23. init: function(oOptions){
  24. oOptions = $.extend({}, m_oDefaults, oOptions);
  25. return this.each(function() {
  26. var oElement = $(this);
  27. oElement.data('validate', {
  28. pluginoptions: oOptions,
  29. fields: []
  30. });
  31. // init operations....
  32. initExampleHelperFunction(oElement);
  33.  
  34. if(null != oElement.data('validate').pluginoptions.ExtraInitFunction){
  35. oElement.data('validate').pluginoptions.ExtraInitFunction();
  36. }
  37. });
  38. },
  39. myInit: function(formName) {
  40.  
  41. },
  42. option: function(oOptions){
  43. $.extend($(this).data('validate').pluginoptions, oOptions);
  44. },
  45. getTestSettingText: function(){
  46. return $(this).data('validate').pluginoptions.sTestSetting;
  47. },
  48. add: function(fieldName, validationType) {
  49. if (!$('input[name="'+fieldName+'"]').length) return false;
  50. if (typeof validationType == "undefined") validationType = 'noEmpty';
  51.  
  52. this.remove(fieldName); // jakby przypadkiem to pole było już walidowane
  53.  
  54. $(this).data('validate').fields.push({fieldName: fieldName, validationType: validationType, formName: this.formName});
  55. return this;
  56. },
  57. remove: function(name) {
  58. var flds = $(this).data('validate').fields;
  59. for (i in flds) {
  60. if (flds[i].fieldName == name) {
  61. $(this).data('validate').fields.splice(i,1);
  62. console.log('Spliced element: '+i+ ' => '+name)
  63. }
  64. }
  65. },
  66. getFields: function(oElement) {
  67. if (typeof oElement != "undefined")
  68. return oElement.data('validate').fields;
  69. },
  70. blur: function(field,oElement) {
  71.  
  72. this.oElement = oElement;
  73. var f = 0, fields = this.getFields(oElement);
  74.  
  75. for (i in fields) {
  76. if (fields[i].fieldName == field) {
  77. if (!this.check(oElement.data('validate').fields[i].fieldName, oElement.data('validate').fields[i].validationType)) {
  78. f++;
  79. }
  80. }
  81. }
  82. return !f;
  83. },
  84. check: function(fieldName, validationType) {
  85. field = this.oElement.find('input[name="'+fieldName+'"]');
  86. return eval('this.'+validationType+'(field)');
  87. },
  88. checkAll: function(oElement) {
  89. var f = 0, fields = this.getFields(oElement);
  90. this.oElement = oElement;
  91. for (i in fields) {
  92.  
  93. if (!this.check(oElement.data('validate').fields[i].fieldName, oElement.data('validate').fields[i].validationType)) {
  94. f++;
  95. }
  96. }
  97. return !f;
  98. },
  99. isObserved: function(name) {
  100. for (i in oElement.data('validate').fields) {
  101. if (oElement.data('validate').fields[i].fieldName == name) {
  102. return true;
  103. }
  104. }
  105. return false;
  106. },
  107. email: function(field) {
  108.  
  109. self = this;
  110.  
  111. var errorSpan = this.oElement.find('.'+field.attr('name')+'.error-info');
  112.  
  113. var regex = /^[a-zA-Z0-9._-]+@([a-zA-Z0-9.-]+\.)+[a-zA-Z0-9.-]{2,4}$/;
  114.  
  115.  
  116. if ( !regex.test(field.val()) ) {
  117. errorSpan.show();
  118. return false;
  119. } else {
  120. errorSpan.hide();
  121. return true;
  122. }
  123.  
  124. },
  125. password: function(field) {
  126. self = this;
  127.  
  128. var errorSpan = $('.'+field.attr('name')+'.error-info');
  129.  
  130. if ( !field.val().length ) {
  131. errorSpan.show();
  132. return false;
  133. } else {
  134. errorSpan.hide();
  135. return true;
  136. }
  137. },
  138. radio: function(field) {
  139. var errorSpan = $('.deliveryAddress.error-info');
  140.  
  141. if (!field.is(':checked')) {
  142. errorSpan.show();
  143. return false;
  144. } else {
  145. errorSpan.hide();
  146. return true;
  147. }
  148. },
  149. noEmpty: function(field) {
  150. self = this;
  151.  
  152. var errorSpan = $('.'+field.attr('name')+'.error-info');
  153.  
  154. if ( !field.val().length ) {
  155. errorSpan.show();
  156. return false;
  157. } else {
  158. errorSpan.hide();
  159. return true;
  160. }
  161. }
  162. };
  163.  
  164. $.fn.validate = function(oFunction) {
  165.  
  166. if (m_aoFunctions[oFunction]) {
  167. return m_aoFunctions[oFunction].apply(this, Array.prototype.slice.call(arguments, 1));
  168. } else if ((typeof oFunction === 'object') || !oFunction) {
  169. return m_aoFunctions.init.apply(this, arguments);
  170. } else {
  171. $.error('Funkcja ' + oFunction + ' nie istnieje w przestrzenii jQuery.myplugin');
  172. }
  173. };
  174. })(jQuery);


Przykłady użycia:

  1. $validatorAddresses = $('form[name="addresses"]').validate();
  2. $validatorAddresses.validate('add', 'deliveryAddress', 'radio'); // czyli konieczność wyboru któregoś adresu z radio
  3. $validatorAddresses.validate('remove', 'deliveryAddress'); // usuniecie z walidacji


Trochę się to różni od tego co napisałem wczoraj. Coś mi sie widać popieprzyło.

Moje szczere oczekiwania odnośnie tego tematu są takie, że ktoś napisze: "Co żeś napisał, powinieneś to zrobić inaczej"...

Pozdrawiam.
trueblue
Cytat(Adi32 @ 2.04.2014, 13:43:13 ) *
Wciąż nie rozumiem return this.each... each to jak mniemam pętla po wszystkich "obiektach obiektu" (tak mi się przynajmniej wydaje) także jaki jest sens takiego zapisu?

this.each iteruje po wszystkich elementach, które podlegają selektorowi, który to podałeś jako parametr przy inicjalizacji pluginu.
Jeśli Twój plugin wywołasz tak:
  1. new Validate('form')

to pętla obejmie wszystkie formularze w dokumencie,
jeśli tak:
  1. new Validate('form.blabla')

to wszystkie formularze z klasą "blabla".
Wszystko co dotyczy danego elementu musisz deklarować jako zmienną wewnątrz pętli each, a nie jako zmienne globalne. Również opcje musisz sobie kopiować do zmiennej w środku each.
Adi32
Z Twojej wypowiedzi wnioskuje, że this.each zaprzecza nieco wieloinstancyjności.
Inaczej mówiąc powiedzmy, że wczytuje objekt:
  1. new Validate($('form'))

Zakłądając, że mam dwa formularze na stronie, mogę się spodziewać, że będą ze sobą kolidować.
Idąc tym tokiem myślenia, wychodzi, że problem wieloinstancyjności nie powinien występować w przypadku takiego uruchomienia:
  1. oForm1 = new Validate($('form[name="deliveryAddress"]'));
  2. oForm2 = new Validate($('form[name="invoiceAddress"]'));

questionmark.gif? Dobrze to rozumiem?

trueblue
Jak to zaprzecza?
Jeśli w this.each będziesz odnosił się do zmiennej "a" zdefiniowanej poza tą pętlą, to przy ustawianiu będziesz ją nadpisywał lub dopisywał do niej (jeśli to tablica), a przy pobieraniu będzie to wspólna zmienna dla każdej iteracji w this.each. Nie ma tu nic magicznego. Każda iteracja this.each wywołuje funkcję. To tak jakbyś deklarował zmienne lokalne w dwóch osobnych funkcjach, albo jedną zmienną globalną i odwoływał się do niej z tych dwóch funkcji.

Co do drugiej kwestii, w takim przypadku, to powinno zapewnić bezkolizyjność.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.