Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [jQuery] Przekazywanie parameteów do funkcji (w pętli)
Forum PHP.pl > Forum > Po stronie przeglądarki > JavaScript
Coach
Mam kod tworzący w pętli setkę divów, każdemu przypisuje zdarzenie onClick.
Niby każdemu z nich powinna się wywołać fumkcja test1 z innym parametrem, ale w praktyce klikając na każdy wywołuje się funkcja przypisana do ostatniego.

  1. $( function(){
  2.  
  3. for ( var i=0; i < 100; i++ ){
  4. var nowydiv = $('<div>' + i + '</div>'); //utworzenie diva
  5. nowydiv.click( function(){ test1( i ) } ); //przypisanie mu funkcji z parametrem
  6. $('body').append( nowydiv ); //dodanie go do dokumentu
  7. }
  8. });
  9.  
  10. function test1( param ){
  11. alert(param);
  12. }


Dlaczego tak się dzieje i jak zrobić, by każdemu divowi był podawany onClick z innym parametrem?

Kasyx
dodaj jakąś klasę do swoich divów, np

[JAVASCRIPT] pobierz, plaintext
  1. var nowydiv = $('<div class="foo">' + i + '</div>');
[JAVASCRIPT] pobierz, plaintext
usun swoją 5 linijkę, bo jest ona strasznie nie na miejscu.

[JAVASCRIPT] pobierz, plaintext
  1. A zamiast tego pod spodem wrzuć:
  2. $('div.foo').click( function() {
  3. var argument = $(this).text();
  4. test1( argument );
  5. });
[JAVASCRIPT] pobierz, plaintext


Poniższe moze zawierać błędy bo pisałem na bieżąco, ale idea jest taka jak przedstawiłem
korro
Dlatego, że zmienne bindowane są w momencie użycia, a nie deklaracji.
Masz nawyk myślenia z języków kompilowalnych.
Przeczytaj wątek: http://forum.php.pl/index.php?showtopic=116641
Pozdrawiam.
Coach
Cytat(korro @ 10.09.2009, 14:06:21 ) *
Masz nawyk myślenia z języków kompilowalnych.


Słusznie, dzięki za pomoc.
Poczytałem nieco o działaniu domknięć
http://www.jibbering.com/faq/faq_notes/closures.html
i było warto.

By dopełnić wątek, skrypt poprawiony

  1.  
  2.  
  3. $( function(){
  4.  
  5. for ( var i=0; i < 100; i++ ){
  6. var nowydiv = $('<div>' + i + '</div>'); //utworzenie diva, to i jest jedynie do identyfikacji, docelowo będzie tu jakiś tekst
  7. nowydiv.click( test1 ); //przypisanie mu nazwy funkcji globalnej obsługującej zdarzenie
  8. nowydiv.data( 'numer', i ); //przypisanie mu obiektu data jquery z adekwatną wartością
  9. $('body').append( nowydiv ); //dodanie go do dokumentu
  10. }
  11. });
  12.  
  13.  
  14. function test1(e){
  15. alert ( $(this).data('numer') ); //nasza dana i jest chowana w obiekcie
  16. }
  17.  
  18.  
l0co
Witajcie, dzisiaj i mnie przyszło walczyć z tym problemem o szukać odpowiedzi. Mimo że pracuję z JS już od lat to dałem się złapać w tę pułapkę. A jaki mi się zdarza coś takiego to lubię to "zgłębić dogłębnie"

Przy okazji zaprezentuję inne rozwiązanie problemu niż bindowanie zmiennej do propertisu obiektu, gdyż w moim przypadku nie chodzi o event handler i nie ma żadnego obiektu. W moim konkretnym przypadku akurat odraczam wykonywanie pewnych czynności na czas po wykonaniu innych czynności (za pomocą domknięć), ale żeby nie komplikować można takie odroczenie zaprezentować za pomocą timeoutu.

Kod bazowy który nie działa:

  1. var o = {one: 1, two: 2, three: 3};
  2.  
  3. var myfunc = function(p) {
  4. alert(p);
  5. }
  6.  
  7. var i = 500;
  8. for (n in o) {
  9. setTimeout(function() {myfunc(n)}, i);
  10. i+=500;
  11. }


Wydawało by się że dostaniemy trzy alerty: "one", "two", "three"; a dostajemy trzy alerty "three".

Teraz poprawka, która "naprawia" kod u góry:

  1. for (n in o) {
  2. function makeClosure(m) {
  3. setTimeout(function() {myfunc(m)}, i);
  4. };
  5. makeClosure(n);
  6. i+=500;
  7. }


Czyli sposób rozwiązania problemu - owinąć nasz kod korzystający ze zmiennej w pętli w inną funkcję.

I teraz pytanko, korro napisał "Dlatego, że zmienne bindowane są w momencie użycia, a nie deklaracji. " co nie do końca rozumiem. Oto jak rozumiem ten problem:

  1. pętla nie tworzy nowego scope zatem w momencie tworzenia funkcji anonimowej w timeoucie wykorzystywany jest bieżący scope na którym leży nasza zmienna "n"
  2. przy każdym wywołaniu funkcji, korzystamy z naszego scopu ze zmienną "n" która wskazuje na ostatnią wartość "n" dlatego, że nie nigdy był stworzony nowy obiekt scope, tylko aktualizowany bieżący scope aktualną wartością "n"
  3. dodanie funkcji owijającej nasz wrażliwy kod spowodowało stworzenie nowego scope dla każdego wywołania funkcji anonimowej, skąd dostępna jest nowa zmienna "m"


Jeśli tak jest, to błąd myślowy jaki popełniłem tutaj jest taki, że założyłem że nowy scope chain tworzony jest w momencie tworzenia nowej funkcji (w sensie stworzenia wszystkich obiektów w chainie), natomiast w rzeczywistości w kontekście wyżej istnieje już nadrzędny scope chain który jest wykorzystany dla kontekstu nowej funkcji (w sensie wykorzystania tych samych instancji scope) i tylko dodawana jest do niego nowo tworzona instacja scope dla nowego kontekstu.

Czy tak jest w rzeczywistości?
zegarek84
w 90% masz, ale tylko w 90%...
Cytat(l0co @ 1.05.2010, 11:48:05 ) *
3. dodanie funkcji owijającej nasz wrażliwy kod spowodowało stworzenie nowego scope dla każdego wywołania funkcji anonimowej, skąd dostępna jest nowa zmienna "m"
....
Czy tak jest w rzeczywistości?

pomijając fakt, iż wywołanie anonimowej funkcji nie działało i chyba dalej nie działa w IE to najważniejszy tutaj jest fakt, iż funkcja w JS w zależności od sposobu użycia jest albo obiektem albo zwykłą funkcją - jeśli zrobi się tak, że jest zwykłą funkcją to korzysta z aktualnej wartości zmiennej... jeśli zrobić tak, bu funkcja była obiektem to jako parametr podpina się inne zmienne pod setTimeoute/setInterval gdyż podaje się inny obiekt z innymi zmiennymi prywatnymi [zmienne w nawiasach funkcji są prywatne tak jakbyś w ciele funkcji przed zmienną napisał var - jeśli nie napiszesz var zmienna londuje w obiekcie window...]

tutaj podam 2 rozwiązania - jedno niemal Twoje, jednak funkcja nie powinna być deklarowana co przebieg pętli to przedewszystkim i drugi ważny argument to taki, by nie podawać funkcji anonimowej jako parametr setTimeout/setInterval...

w pasku przeglądarki wklei i kliknij enter [javascript ma być razem!! - tutaj na forum zawsze rozdziela]:
[JAVASCRIPT] pobierz, plaintext
  1. java script:
  2. var o = {one: 1, two: 2, three: 3};
  3. var myfunc = function(p) {
  4. alert(p);
  5. }
  6. var i = 500;
  7. var opakowanie = function(p, i){
  8. var prywatna_metoda = function (){
  9. myfunc(p);
  10. };
  11. setTimeout(prywatna_metoda, i);
  12. };
  13. for (n in o) {
  14. opakowanie(n, i); /* opakowanie tutaj staje się obiektem */
  15. i+=500;
  16. }; void(0);
[JAVASCRIPT] pobierz, plaintext


powyższe to niemal Twoje rozwiązanie jednak poprawione tak jak trzeba... a poniżej jeśli wolisz inaczej pisać... :
[JAVASCRIPT] pobierz, plaintext
  1. java script:
  2. var o = {one: 1, two: 2, three: 3};
  3. var myfunc = function(p) {
  4. this.alarm=function(){
  5. alert(p);}
  6. }
  7. var i = 500;
  8. var temp;
  9. for (n in o) {
  10. temp = new myfunc(n);
  11. setTimeout(temp.alarm, i);
  12. i+=500;
  13. }; void(0);
[JAVASCRIPT] pobierz, plaintext


PS.
a propo bindowania to mi się kojarzy bardziej z podpinaniem referencji - gdzie zmienna jest użyta dopiero gdy potrzebna winksmiley.jpg
l0co
Dziękuję za tę odpowiedź, zmusiła mnie do zastanowienia się nad jeszcze kilkoma aspektami. Każde z tych rozwiązań różni się nieco i warto sobie je przeanalizować, jednak "general concept" pozostaje taki sam jak mi się wydaje - czyli trzeba powołać nowy scope, powołując nową funkcję, na który trzeba jeszcze raz odłożyć ten parametr.

Faktycznie lepiej było wykorzystać funkcję utworzoną przed pętlą, zamiast tworzyć w pętli za każdym razem nową. Jednak w IE działa bez problemu.

Z drugiej strony - zastanawiałem się jeszcze czy funkcja deklarowana przez var param=... czy tylko przez function() tutaj ma znaczenie, ale chyba nie, bo scope dla tych funkcji jest taki sam, różni się to tylko momentem utworzenia obiektu funkcji.

Dlaczego piszesz żeby nie podawać funkcji anonimowej dla setTimeout?
zegarek84
Cytat(l0co @ 1.05.2010, 13:17:49 ) *
Dlaczego piszesz żeby nie podawać funkcji anonimowej dla setTimeout?
skoro działa na IE (niestety na tym laptopie nie mam pod ręką) to mój błąd za co przepraszam - nie wiem skąd ale jakoś mi w pamięci mi utkwiło, że anonimowa funkcja w IE nie działa...
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.