Funkcja onclick i tablica

0

Witam
Jestem początkującym programistą, mam problem z wyświetleniem diva z zawartością pliku php i wysłanie z nim informacji GET. Poniższy kod sprawdza się, lecz w większej ilości albumów będzie sporo linijek kodu

<script type="text/javascript">
    var ObiektXMLHttp = false;
    if (window.XMLHttpRequest) {
        ObiektXMLHttp = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        ObiektXMLHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    function getData(zrodlo) {
        if (ObiektXMLHttp) {
            ObiektXMLHttp.open("GET", zrodlo);
            ObiektXMLHttp.onreadystatechange = function() {
				if (ObiektXMLHttp.readyState == 1) {
				document.getElementById('tresc_album2').innerHTML = "Ładowanie..."; }
                if (ObiektXMLHttp.readyState == 4) {
                    document.getElementById('tresc_album2').innerHTML = ObiektXMLHttp.responseText;
                }
            }
            ObiektXMLHttp.send(null);
        }
    } 
	
window.onload = Laduj;
function Pokaz(x)
{
		getData('album.php?album='+x);
		var div = document.getElementById('szary_album');
		var div2 = document.getElementById('tresc_album');
		div.style.display = "block";
		div2.style.display = "block";
}

function Pokaz_z()
{
	var div = document.getElementById('szary_album');
	var div2 = document.getElementById('tresc_album');
	div.style.display = "none";
	div2.style.display = "none";
}

function Laduj()
{
	document.getElementById('album_zamknij').onclick = Pokaz_z;
	
         document.getElementById('album_otworz1').onclick = function() { Pokaz(1) };
	document.getElementById('album_otworz2').onclick = function() { Pokaz(2) };
	document.getElementById('album_otworz3').onclick = function() { Pokaz(3) };
	document.getElementById('album_otworz4').onclick = function() { Pokaz(4) };

}
</script>
<?php

echo"
<div id='tresc'>
	<div id='album1'>
		obrazek do albumu 1<br> 
		<div id=\"album_otworz1\">album1</div><br>
		<div id=\"album_otworz2\">album2</div><br>
		<div id=\"album_otworz3\">album3</div><br>
		<div id=\"album_otworz4\">album4</div><br>
	</div>
</div>
";

echo"
<div id='tresc_album' style=\"display: none;\">
	<div id=\"album_zamknij\">zamknij</div>
	<div id='tresc_album2'>
	Nie wybrano album.
	</div>
</div>
<div id='szary_album' style=\"display: none;\">
</div>
";

?>

chcę zastąpić kod:

         
        document.getElementById('album_otworz1').onclick = function() { Pokaz(1) };
	document.getElementById('album_otworz2').onclick = function() { Pokaz(2) };
	document.getElementById('album_otworz3').onclick = function() { Pokaz(3) };
	document.getElementById('album_otworz4').onclick = function() { Pokaz(4) };

kodem:

	var Tablica = new Array(1, 2, 3, 4)
	for (n = 0; n < Tablica.length; n++){
		document.getElementById('album_otworz'+Tablica[n]).onclick = function() { Pokaz(Tablica[n]) };
		return false;
	}

niestety tylko przy kliknięciu na div z id album_otworz1 działa, a na reszcie nie działa.
Proszę o pomoc

4

Ha! Klasyczny problem z domknięciem!

Bardzo dobrze, że chcesz zastąpić stary kod nowym -- ten stary jest obrzydliwy :).

Odsyłam do książki "JavaScript - Mocne strony" Douglasa Crockforda. Bo masz w kodzie parę problemów świadczących o nieoptymalnym wykorzystaniu języka. Dobrze, że starasz się dbać o kod i polepszanie swoich umiejętności, ale to staranie powinno Cię też doprowadzić do przeczytania wspomnianej książki. Krótka, niedroga, świetna.

Póki co postaram Ci wytłumaczyć, co jest nie tak.

Chcesz mieć w funkcji Laduj pętlę z licznikiem n. Wewnątrz pętli chcesz przypisywać zdarzeniom onclick funkcje anonimowe (tj. funkcje bez nazwy, tworzone w locie i od razu przypisywane do onclick), które korzystają z licznika n.

Po pierwsze sporym błędem jest to, że nie zadeklarowałeś zmiennej n. Nie napisałeś w pętli "for (var n = 0; n < ... itd.)". Brakuje Ci var i n staje się zmienną globalną, a nie lokalną. Samo wstawienie var nie naprawi jednak Twojego problemu.

Po drugie, masz tam wstawione "return false" w zupełnie nie tym miejscu, co trzeba. Opracuj sobie lepszy standard kodowania, jeśli to Ci tutaj doskwiera. Nie pisz funkcji w jednej linii! Rozumiem, że chciałeś mieć "return false" wewnątrz funkcji przypisanych do onclick, a masz na końcu pętli funkcji Laduj.

Chciałeś mieć coś takiego:

        for (var n = 0; n < Tablica.length; n++){
                document.getElementById('album_otworz'+Tablica[n]).onclick = function() {
                  Pokaz(Tablica[n])
                  return false;
                };
        }

A masz coś takiego:

        for (var n = 0; n < Tablica.length; n++){
                document.getElementById('album_otworz'+Tablica[n]).onclick = function() {
                  Pokaz(Tablica[n])
                };
                return false;
        }

Czyli dlatego działa Ci to tylko dla pierwszego albumu, bo wykonuje się tylko jedna -- pierwsza -- iteracja pętli. Potem jest return false i funkcja Laduj kończy działanie.

Nawet jeśli to poprawisz to jednak kod nie będzie Ci działał.

Korzystasz, być może nieświadomie, z tzw. domknięcia -- bardzo potężnej właściwości JavaScriptu i wielu innych języków funkcyjnych. Domknięcie polega generalnie na tym, że funkcja wewnętrzna widzi zmienne z funkcji zewnętrznej. W Twoim przypadku funkcje przypisane do onclick widzą zmienną n z funkcji Laduj nawet gdy funkcja Laduj skończyła już dawno działanie! (Bo funkcja Laduj działa na początku, a ktoś po np. 10 sekundach może kliknąć i uruchomić funkcję przypisaną do onclick).

Sęk w tym, że na każde wywołanie funkcji zewnętrznej powstaje tylko jedna kopia jej zmiennych. I wszystkie funkcje wewnętrzne widzą tę samą kopię. Czyli wszystkie funkcje przypisane do onclick widzą jedną i tę samą zmienną n. Tę z jednego, jedynego wywołania funkcji Laduj. Ponieważ funkcje onclick są odpalane np. kilka sekund po zakończeniu funkcji Laduj, a więc po zakończeniu pętli z funkcji Laduj, wszystkie funkcje onclick będą widziały wartość n ustawioną w ostatniej iteracji pętli. Będzie to 5.

Przed przeczytaniem książki Crockforda możesz uniknąć takich rzeczy wydzielając sobie funkcję, która będzie podpinała jedno, n-te zdarzenie. Nazwij sobie tę funkcję np. InicjalizujAlbumNumer(n), gdzie n to będzie parametr. Ta funkcja przypisze onclick do n-tego albumu:

function InicjalizujAlbumNumer(n) {
  document.getElementById('album_otworz' + n).onclick = function() {
    Pokaz(n);
    return false;
  };
}

Zauważ, że tutaj korzystamy z domknięcia: funkcja przypisana do onclick korzysta z parametru n funkcji zewnętrznej. Ale nie ma tu już żadnej pętli, więc nic nam się nie spieprzy.

Pętla będzie w funkcji Laduj i w tej pętli wywołamy dla każdego numeru albumu naszą funkcję InicjalizujAlbumNumer(n):

function Laduj() {
  var Tablica = new Array(1, 2, 3, 4)
  for (var i = 0; i < Tablica.length; i++){
    InicjalizujAlbumNumer(Tablica[i]);
  }
}

Zauważ proszę, że zmienna Tablica w ogóle nie jest nam potrzebna. Używamy jej tylko dla indeksów, które idą po kolei, a jedyny szkopuł w tym, że zaczynają się od 1. Można napisać od razu odpowiednią pętlę:

function Laduj() {
  for (var i = 1; i <= 4; i++){
    InicjalizujAlbumNumer(i);
  }
}

Zarówno 1, jak i 4 moglibyśmy przechowywać sobie w zmiennych (i traktować te zmienne jak stałe), coby nie używać brzydkich, magicznych liczb, ale to już pozostawiam w Twojej gestii.

0

dzięki za naprowadzenie na lepszą drogę programowania. Jak do tej pory korzystałem z gotowych "produktów", teraz natomiast chciałem połączyć kilka linijek kodu,niespodziewanie pojawiło się wiele niewiadomych. Zakupie tę książkę

4

@kikarto:
Z tym returnem nie tam gdzie trzeba to w sumie głupia sprawa, prosty błąd -- czasem się zdarzają, ale zwykle dobre standardy kodowania (formatowanie kodu) temu zaradzą. Natomiast pocieszę Cię, że te domknięcia to akurat jeden z trudniejszych do ogarnięcia elementów języka. Rzecz jest w zasadzie bardzo prosta i używa się tego naturalnie, ale wychodzą problemy w takim przypadku jak Twój. Dlatego tę stosunkowo prostą rzecz trzeba naprawdę zrozumieć, cobyś nie klął potem pod nosem za każdym razem gdy będziesz przypisywał w pętli jakieś funkcje pod onclick. Domknięcia, mimo prostoty są bardzo potężne i używa się ich w JavaScripcie non stop. Ci, którym nie chce się ich nauczyć, używają ich sporadycznie i nieświadomie. Chyba ta przydatność, duża moc ekspresyjna i brak odpowiednika w PHP (choć wprowadzają taką słabszą wersję domknięć do nowej wersji) sprawia, że z ogarnięciem domknięć ludzie miewają pewne problemy.

Pocieszam zatem w ten sposób, że niemalże nie ma innych podobnych kruczków -- przydatnych, ale nieintuicyjnych dla kogoś kto przyszedł z innego języka.

A grunt to chcieć się uczyć i naprawdę pomalutku zwiększać umiejętności. Nowego dla Ciebie języka nigdy z początku nie będziesz wykorzystywał optymalnie, ale szkopuł w tym, żeby się go stopniowo uczyć. Wymieniona książka jest w tym bardzo pomocna. Gotowych rozwiązań do wklejenia na stronę tam w zasadzie nie ma, ale zasady pisania kodu opisane w książce będą Ci się przydawały w każdej chwili gdy będziesz pisał jakikolwiek JavaScript.

Jeszcze na zachętę, taki skrócik do definiowania tablic. Ludzie, szczególnie ci z PHP, piszą często tak:

var tablica = new Array('foo', 'bar', 'baz');

Ale JavaScript oprócz konstruktora Array posiada też tzw. literał tablicowy. Bardzo zwięzły, bardzo wygodny zapis. To samo, co powyżej, można zapisać za jego pomocą tak:

var tablica = ['foo', 'bar', 'baz'];

I to dokładnie to samo. W książce zalecają używać wyłącznie literał tablicowy, czyli te nawiasy -- ja sam też tylko tego używam. A niektórzy nie znający JavaScriptu piszą jeszcze tak:

var tablica = new Array();
tablica[0] = 'foo';
tablica[1] = 'bar';
tablica[2] = 'baz';

Porównując to z jednolinijkowym zapisem przy pomocy literału tablicowego widać chyba, że warto nieraz przeczytać jakąś książkę nie tyle o konkretnych, przydatnych skryptach, ile o samym języku ;).

1 użytkowników online, w tym zalogowanych: 0, gości: 1