érdekes JS probléma: a script először rosszul működik (MINDEN ESETBEN!), majd megjavul...
A problémát röviden a kód alatt leírom. FF3 alatt néztem. A komplett kód, ki is lehet próbálni (protoype.js):A hiba reprodukálása:
1. Kattints egymás után kétszer a Delay1 szövegre, majd gyorsan a Delay2 szövegre. Ekkor 4 új sor lesz. Normális működés esetén az 1. és a 3. sornak számolnia kellene. És 10 másodperc alatt mindenhol azt kéne kiírnia, hogy kész, de ezzel ellentétben 20 másodperc alatt megy végig és egyesével.
2. Várd végig, míg mind a négy sornál azt írja ki, hogy kész. Most ismételd meg az előző lépést. Kettőt kattints a Delay1-en és kettőt a Delay2-n. Láss csodát, normálisan működik.
A probléma röviden: Van egy időzítő (Timer), ami az oldal betöltése után elindul és fél másodpercenként meghívogatja a neki átadott objektumokat (egészen pontosan az objektumok runTimer() fv-ét). Itt a példában két objektumot kap kattintásra, amik azt csinálják, hogy várakoznak 5 másodpercet, annyiszor, ahányszor rákattintasz. Amikor az elsőre kattintok, akkor elindul a számláló. Ha ugyanekkor a másodikra, akkor annak is számolnia kellene! De nem ezt csinálja :( A this.current-re az első objektum current-jét használja! De csak az elején. Ha végigvárom a kattintások eredményeit, majd újra kattintok, akkor már minden jól működik. Miért??? A logban látszik, hogy egy darabig a másodikként elindított várakoztató az elsőnek az objektumával "dolgozik". Ugyanakkor néha a saját delayStart változóját használja, néha az elsőnek létrehozott objektumét... IE alatt nem is akar működni, a hibakeresője teljesen használhatatlan és értelmetlen üzeneteket ad. Nem a hiba valódi okát adja meg. Most a hiba az én gépemben van (telepítsem újra? :D ), vagy a scriptben?
Ha a hibát reprodukálod, a logban látható, hogy a Delay1 (d1) és a Delay2 (d2) ugyanazzal az elemmel foglalkozik (szögletes zárójelbe írt szám), tehát a this.current mindkettőjük esetében ugyanaz! DE! A this.delayStart (sima zárójelbe írt szám) meg mégsem ugyanaz, azt jól kezeli, tehát nem keveri a két objektum adatát.
Tehát: hol a hiba, hogy ez így működik? (tegnap este még működött rendesen, ma mikor bekapcsoltam újra a gépet, azután kezdte el ezt)
IE alatt tegnap sem működött, tudna vki segíteni, hogy miért nem? Nem vagyok vmi nagy JS guru :( (még :) )
Az "Objektumorientált JavaScript programozás a felszín fölött" cikket olvastam, úgyhogy ha ott van válasz, csak én siklottam el felette, akkor vissza lehet rá utalni, és akkor előre is elnézést kérek :)
■
<html>
<head>
<script language="javascript" src="prototype.js"></script>
<script type="text/javascript">
/**
* Timer
* Időzítő. Amint betöltődött az oldal fél másodpercenként lefut.
*/
var Timer = {
// Ebben a tömbben tárolja, hogy mely objektumok runTimer() fv-t kell meghívni minden fél másodpercben.
container: [],
// Ebben a változóban követjük, hogy befejeződött-e az előző hivás, és ha nem, akkor nem hívható kétszer egymás után!!!
isRun: false,
// Itt nézzük, hogy el lett-e már indítva az időzítő
isStarted: false,
// Itt adunk hozzá új visszahivandó objektumot.
add: function(element, overwrite) {
added = this.hasAdded(element);
if(!overwrite && added) {
return false;
} else if(added) {
this.remove(element);
}
this.container[this.container.length] = element;
return true;
},
// Itt távolítjuk el a megadott objektumot, ha arra már nincs szükség.
remove: function(element) {
this.container = this.container.without(element);
},
// Megnézi, hogy a megadott elem hozzá lett-e már adva.
hasAdded: function(element) {
return (this.container.indexOf(element)>=0);
},
// Elindítja az időzítőt
start: function(event) {
// Csak akkor lehet elindítani az időzítőt, ha az még nincs elindítva.
if(!this.isStarted)
{
window.setInterval('Timer.run()', 500);
this.isStarted = true;
}
},
// Ezt hívja meg fél másodpercenként
run: function() {
if(!this.isRun) {
// Elkezdődik a tömb bejárása
this.isRun = true;
// Az objektumoknak átadja a az aktuális időpontot
d = new Date();
// Bejárja a container tömböt és egyesével meghívja az ott lévő objektumok callTimer() fv-ét
this.container.each(function(element) {
element.callTimer(d);
});
// Befejeződött a tömb bejárása
this.isRun = false;
}
}
}
// Várakoztató "osztály". Több ilyen is lehet. Akkor kerül egy "várakoztatóba" két objektum,
// ha a második csak akkor kezdődhet el számolni, ha az előző már lefutott
var Delayer = Class.create({
// Mikor kezdett el várakozni a this.current elem.
delayStart: null,
// Az aktuálisan várakozó objektum
current: null,
// Az objektum lista, ami várakozik
container: [],
// Éppen várakozik-e vmi
isRun: false,
name: null,
initialize: function(name) {
this.name = name;
},
start: function(element) {
this.container[this.container.length] = element;
Timer.add(this, false);
},
// Eltávolít egy várakozó objektumot
cancel: function(element) {
this.container = this.container.without(element);
},
// Alaphelyzetbe állítja a "várakoztatót"
reset: function(element) {
// Törli az elemet a listáról
if(typeof(element)!='undefined') {
this.cancel(element);
}
this.current = null;
this.delayStart = null;
if(document.test) {
document.test.logta.value+="----- Reset: "+this.name+"\n";
}
},
// Eggyel továbblépteti a várakozó elemek listáján.
next: function(d) {
// Ha nincs több elem, akkor kilép
if(this.container.length==0) {
this.reset();
return false;
}
this.current = this.container.first();
this.delayStart = d;
return true;
},
callTimer: function(d) {
// Ha az aktuális elem null és nincs tovább
if(this.current==null && !this.next(d)) {
Timer.remove(this);
return false;
}
document.test.logta.value+=d.getTime()+": "+this.name+" ("+this.delayStart.getTime()+") ["+this.current.n+"]\n";
// Ennyi másodpercet kell várnia az adott objektumnak
w = this.current.wait;
// Ennyi másodperc van még hátra
distance = w-parseInt((d.getTime()-this.delayStart.getTime())/1000)-1;
if(distance>0) {
this.current.status('Hátra van:'+distance+' s');
} else {
this.current.run(d);
this.reset(this.current);
}
},
});
var Obj = Class.create({
// Az objektum sorszáma
n: 0,
// Ennyi másodpercet várjon
wait: 5,
// ahova írkálnia kell
html: null,
initialize: function(n) {
this.n = n;
this.html = new Element('div', {'id': 'obj_'+n}).update('init');
document.body.appendChild(this.html);
},
run: function(d) {
this.status('Kész: '+this.n);
},
status: function(msg) {
this.html.update(msg);
}
});
var counter = 0;
var d1 = new Delayer('d1');
function startDelay1() {
document.test.logta.value+="+++++ Klikk: Delayer1\n";
d1.start(new Obj(counter));
counter++;
}
var d2 = new Delayer('d2');
function startDelay2() {
document.test.logta.value+="+++++ Klikk: Delayer2\n";
d2.start(new Obj(counter));
counter++;
}
</script>
</head>
<body>
<script>
document.body.onload = Timer.start();
</script>
<form name="test">
<textarea name="logta" cols="100" rows="40" style="float: right"></textarea>
</form>
<div id="message" onclick="javascript: startDelay1()">Delayer1</div>
<div id="message2" onclick="javascript: startDelay2()">Delayer2</div>
</body>
</html>
1. Kattints egymás után kétszer a Delay1 szövegre, majd gyorsan a Delay2 szövegre. Ekkor 4 új sor lesz. Normális működés esetén az 1. és a 3. sornak számolnia kellene. És 10 másodperc alatt mindenhol azt kéne kiírnia, hogy kész, de ezzel ellentétben 20 másodperc alatt megy végig és egyesével.
2. Várd végig, míg mind a négy sornál azt írja ki, hogy kész. Most ismételd meg az előző lépést. Kettőt kattints a Delay1-en és kettőt a Delay2-n. Láss csodát, normálisan működik.
A probléma röviden: Van egy időzítő (Timer), ami az oldal betöltése után elindul és fél másodpercenként meghívogatja a neki átadott objektumokat (egészen pontosan az objektumok runTimer() fv-ét). Itt a példában két objektumot kap kattintásra, amik azt csinálják, hogy várakoznak 5 másodpercet, annyiszor, ahányszor rákattintasz. Amikor az elsőre kattintok, akkor elindul a számláló. Ha ugyanekkor a másodikra, akkor annak is számolnia kellene! De nem ezt csinálja :( A this.current-re az első objektum current-jét használja! De csak az elején. Ha végigvárom a kattintások eredményeit, majd újra kattintok, akkor már minden jól működik. Miért??? A logban látszik, hogy egy darabig a másodikként elindított várakoztató az elsőnek az objektumával "dolgozik". Ugyanakkor néha a saját delayStart változóját használja, néha az elsőnek létrehozott objektumét... IE alatt nem is akar működni, a hibakeresője teljesen használhatatlan és értelmetlen üzeneteket ad. Nem a hiba valódi okát adja meg. Most a hiba az én gépemben van (telepítsem újra? :D ), vagy a scriptben?
Ha a hibát reprodukálod, a logban látható, hogy a Delay1 (d1) és a Delay2 (d2) ugyanazzal az elemmel foglalkozik (szögletes zárójelbe írt szám), tehát a this.current mindkettőjük esetében ugyanaz! DE! A this.delayStart (sima zárójelbe írt szám) meg mégsem ugyanaz, azt jól kezeli, tehát nem keveri a két objektum adatát.
Tehát: hol a hiba, hogy ez így működik? (tegnap este még működött rendesen, ma mikor bekapcsoltam újra a gépet, azután kezdte el ezt)
IE alatt tegnap sem működött, tudna vki segíteni, hogy miért nem? Nem vagyok vmi nagy JS guru :( (még :) )
Az "Objektumorientált JavaScript programozás a felszín fölött" cikket olvastam, úgyhogy ha ott van válasz, csak én siklottam el felette, akkor vissza lehet rá utalni, és akkor előre is elnézést kérek :)
Most komolyan?
Ez már az egyszerűsítés után van :)
Az eredeti kód ennél jóval hosszabb, ez már gyomlálás után van, de az észrevételed után tovább kopasztottam, most már tényleg csak a lényegi rész van benne.
IE probléma megoldva. A bemásolt kód 138. sorában van egy vessző, ami nem tetszett neki. Persze a világért sem ezt írta ki... Szinte soronként elkezdtem átmásolni egy másik fájlba a forrást, úgy találtam meg a hibát.
Átírtam, így már működik IE alatt is, és ott is ezt a tünetet produkálja. Itt az új kód:
A gond tehát az, hogy létrehozok két "várakoztatót". Legyenek ezek mondjuk jegyellenőrök. A jegyellenőrnek meg van mondva, hogy a biztonsági emberke 5 másodperc alatt ellenőriz le egy vendéget. A jegyellenőr feladata, hogy pontosan 5 másodperc múlva engedje tovább a vendéget, addig ne! A jegyellenőr és a biztonsági emberke egyszerre csak egy emberrel tud foglalkozni, így ha egy ellenőrhöz 2-3 ember áll be a sorba, akkor szépen mindegyiknek ki kell várnia a sorát. (a container tömbbe kerülnek az "emberkék")
Esetünkben viszont a gyorsabb haladás miatt két jegyellenőr van létrehozva, így az emberek két helyre is állhatnak. Amikor a Delay 1-re kattintasz, akkor egy emberkét beállítasz a sorba az 1. jegyellenőrhöz, a Delay 2-nél pedig a 2. jegyellenőrhöz.
Ha 4 emberkéd van, és 2-2 emberkét állítasz be a sorba, akkor 10 másodperc alatt mindnek be kéne jutnia. Ellenben a scriptnél a következő történik:
Beállítasz 2-2 embert, a két jegyellenőrhöz. Csakhogy nem külön-külön elkezdenek foglalkozni a sajátjaikkal, hanem mindkettő ugyanazzal az emberrel foglalkozik! A 'this.container' változó mintha mindkét objektumban "megegyezne", így mikor hozzáadok egy-egy emberkét mindkét sorhoz, abból nem két sor lesz, hanem 1! De csak amíg be nem jut mindegyik ember, és üres nem lesz mindkét sor. Mert ha ekkor megint hozzáadok 1-1 embert a jegyellenőrökhöz, akkor már 2 sor fog kialakulni és helyesen működik, tehát a 'this.container' már mindkét objektumban más!
Máshogy, nagyon lebutítva, csak a tünetre koncentrálva:
Ugyanaz
Nem
Nem értem
+++++ Klikk: Delayer[12]
illetve egy init felirat jelenik meg és ennyi.
prototype
Erre nem is gondoltam...
prototype
prototype
onload
Valóban