2009-07-22 23 views
3

So habe ich noch ein weiteres kleines JavaScript-Problem mit meinem aktuellen Projekt: JS-Ereignisse werden nicht in der Reihenfolge, die ich erwarte sie zu feuern.Javascript-Ereignisse, die nicht in der erwarteten Reihenfolge ausgelöst werden

Angenommen, ich habe ein Textfeld mit einem onBlur-Ereignis, das einen AJAX-Aufruf auslöst, der einige Berechnungen auf dem Server durchführt (ich verwende .NET 3.5's WebServices mit JSON). Die Callback-Methode füllt eine Seite mit Textfeldern mit Ergebnissen aus der Berechnung.

Ich habe auch eine Schaltfläche mit einem onClick-Ereignis, die eine andere Berechnung initiiert, aber es hängt davon ab, die onBlur -Ereignis (s) des Textfelds abgeschlossen, bevor die Ergebnisse seiner onClick-Ereignis gültig sein werden.

Ich habe einen einfachen Mutex an Ort und Stelle, der ziemlich gut funktioniert, bis die folgende Situation: Der Benutzer ändert einen Wert in einem der Textfelder, hat es aber noch nicht verlassen, so dass der Fokus immer noch auf dem Textfeld ist. Während das Textfeld den Fokus hat, klicken sie auf eine Schaltfläche mit einem onClick-Ereignis. In den meisten Fällen löst der Browser (in diesem Fall IE7) das onClick-Ereignis der Schaltfläche VOR dem onBlur-Ereignis des Textfelds aus!

Das verursacht einige Verwüstung, und ich habe keine Ahnung, wie die Reihenfolge der Ereignisse, die abgefeuert werden, erzwingen. Mein kleiner Mutex funktioniert wie folgt: Wenn eines der Ereignisse ausgelöst wird, setze ich ein Flag, das bedeutet "Warte auf den Rückruf". Alle nachfolgenden Ereignisse, die ausgelöst werden, überprüfen die Flagge, bevor sie etwas tun. Setzen Sie das Flag im Callback auf false zurück.

Also, irgendwelche Ideen? Ich denke, ich möchte fast alle Web-Service-Aufrufe synchron machen, aber ich weiß nicht, ob es einen einfachen Weg gibt, das zu tun.

Andere Dinge, die ich habe versucht, oder Dinge sollten Sie wissen: - Deaktivieren der Schaltflächen in der onBlur der Textfelder - ihre onClicks noch Feuer, bevor sie Behinderte bekommen - Denken Sie daran, dies geschieht nur, wenn eine Bearbeitung Textfeld und sofort auf eine Schaltfläche klicken. Wenn der Benutzer auf einen leeren Platz klickt, um die onBlur des Textfelds auszulösen, bevor er auf die Schaltfläche klickt, funktioniert alles perfekt.

EDIT:

Nach einigen Kommentaren und mehr Tests ist hier, was ich möchte in der Lage sein zu tun:

Im onBlur des Textfeldes, möchte ich auf die Schaltfläche deaktivieren, so dass Das Ereignis onClick wird nicht ausgelöst. Wenn ich das jedoch code, wird das onBlur-Ereignis ausgelöst, ich kann sehen, dass die Schaltfläche deaktiviert wird, aber ihr onClick-Ereignis wird weiterhin ausgelöst.

Hier ist ein Beispielcode. Manchmal tritt das Ereignis onClick auf und manchmal nicht. Wenn Sie das setTimeout auf 500 ms aufstocken, scheint es immer zu verhindern. Vielleicht ist dies eine Option ...

<html> 
<head> 
<script type="text/javascript"> 

var onClickFlag = false; 

function whatHappensOnFocus(el) { 
    log('whatHappensOnFocus(): TextField focused.'); 
    onClickFlag = false; 
    log('whatHappensOnFocus(): Set onClickFlag = false'); 
} 

function whatHappensOnBlur(el) { 
    log('whatHappensOnBlur(): TextField blurred.'); 
    document.getElementById('theButton').disabled = true; 
    log('whatHappensOnBlur(): Disabled the Button'); 
    setTimeout(function() { someTestFieldFunction(); log('whatHappensOnBlur(): OnBlur callback called: someTestFieldFunction()'); }, 50); 
    log ('whatHappensOnBlur(): setTimeout for someTestFieldFunction() for 50ms'); 
} 

function log(msg) { 
    document.getElementById('log').value += ('[' + (new Date().getTime()).toString() + '] ' + msg + '\n'); 
} 

function someTestFieldFunction() { 
    log('someTestFieldFunction(): Inside someTestFieldFunction()'); 
    log('someTestFieldFunction(): Test: onClickFlag = ' + onClickFlag); 
    //alert('you blurred the text field.'); 
    //alert('onClickFlag = ' + onClickFlag); 
    setTimeout(enableButton, 50); 
    log('someTestFieldFunction(): setTimeout for enableButton()'); 
} 

function enableButton() { 
    document.getElementById('theButton').disabled = false; 
    log('enableButton(): Button re-enabled'); 
    //onClickFlag = false; 
} 

function whatHappensOnClick(el) { 
    log('whatHappensOnClick(): the Button onClick fired'); 
    someFunction(); 
    log('whatHappensOnClick(): someFunction() called'); 
} 

function whatHappensOnMouseDown(el) { 
    log('whatHappensOnMouseDown(): inside whatHappensOnMouseDown()'); 
    onClickFlag = true; 
    log("whatHappensOnMouseDown(): set onClickFlag = true"); 
} 

function someFunction() { 
    log('someFunction(): inside someFunction()'); 
    log('someFunction(): You have successfully clicked the button'); 
    //alert("you clicked me! this shouldn't happen!") 
} 

</script> 
</head> 
<body> 
<form id="testform"> 
<input type="text" size="10" onfocus="whatHappensOnFocus(this);" onblur="whatHappensOnBlur(this);" /> 
<br /> 
<br /> 
<input id="theButton" type="button" onmousedown="whatHappensOnMouseDown(this);" onmouseout="onClickFlag = false;" value="calculate" onclick="whatHappensOnClick(this);" /> 
<br /> 
<br /> 
<textarea rows="15" cols="100" id="log" style="overflow: scroll"></textarea> 
<input type="reset" value="clear" /> 
</form> 
</body> 
</html> 

UPDATE:

ich den Code aktualisiert ich gepostet einen Teil der Ausgabe zu reflektieren, so können Sie sehen, wie ich das getestet. Wenn Sie sich auf ein Textfeld konzentrieren und dann auf eine Schaltfläche klicken, wird das Ereignis onMouseDown der Schaltfläche vor dem Ereignis onBlur des Textfelds ausgelöst. Aus diesem Grund kann ich eine Flagge setzen, sodass ich weiß, dass sie versucht hat, auf die Schaltfläche zu klicken. Dann kann ich im onSuccess-Callback-Ereignis des AJAX-Aufrufs, der durch das onBlur-Ereignis des Textfelds ausgelöst wird, sehen, ob das Button-Flag gesetzt ist (also weiß ich, dass sie versucht hat, darauf zu klicken) und dieses Ereignis abbricht (alles andere deaktiviert) Komplett).Es gibt nur ein paar Schaltflächen auf dem Formular, die dieses Verhalten benötigen, also denke ich, dass diese Lösung für jetzt ausreichend sein wird.

+0

Ich habe gerade einen schnellen Test gemacht, und das Blur-Ereignis * immer * feuert zuerst für mich in IE7. –

+0

Wirklich? Ich glaube dir ... lass mich auch einen Schnelltest machen ... –

+0

Ok, du hast Recht. Ich habe die Frage bearbeitet, weil ich immer noch verrücktes Verhalten habe. –

Antwort

0

Der einfachste Weg (und wahrscheinlich der Schlimmste, und von FAR) wäre, das Programm eine gewisse Zeit warten zu lassen, bevor Sie zu dem fortfahren, was Sie von vornherein tun wollten, damit Sie die Reihenfolge kontrollieren konnten der Rückrufe.

Wie gesagt, dies ist wahrscheinlich der schlechteste Weg, aber es ist der einzige, den ich mir vorstellen kann. Hier sollte es aussehen:

function onClickHandler(callback) 
{ 
    setTimeout(callback, 50); 
} 

function onBlurHandler(callback) 
{ 
    //... 
} 

Auf diese Weise würde Ihre Callback-Funktion auf onBlur immer zuerst geschehen.

+1

Bitte sei nicht zu streng zu mir! Ich habe nur versucht zu helfen! –

+0

Ich dachte eigentlich auch an so etwas. Ich denke nicht, dass es die schlechteste Lösung ist, solange es funktioniert. –

+2

Ihre Ehrlichkeit sollte Sie von Down-Stimmen speichern :) –

1

Was js mit der Textbox verursachen Fokus zu verlieren, nachdem es beendet hat, zu tun, was es braucht, um?

0

Ich bezweifle die Reihenfolge der Ereignisse sind die Quelle Ihres Problems. Ich habe gerade versucht, den folgenden Code in IE7, IE8 und Chrome und die Reihenfolge der Ereignisse ist immer blur dann klicken.

Wenn ich Ihnen richtig folge, tun Sie einige Anrufe, die andere Anrufe erfordern, die vorher abgeschlossen werden. Dies scheint kein sehr guter Ansatz zu sein, und bei diesem Ansatz werden Sie entweder auf Fehler oder eine hohe Komplexität des Codes stoßen.

Bitte versuchen Sie zu erklären, was Sie tun (aus funktioneller Sicht) und versuchen Sie alternative Wege zu finden, anstatt einen komplexen technischen Ansatz zu erarbeiten.

+0

Leider ist das Projekt sehr komplex.Die Felder, die wir auf dem Bildschirm sehen, werden in Gruppen serialisiert oder einzeln an einen Webdienst gesendet, der an Werten bastelt, eine riesige (50kb +) XML-Datei erstellt, die an einen anderen Webdienst weitergeleitet wird, der mit einer COM-Anwendung kommuniziert, die Berechnungen für das XML durchführt Daten. Die XML-Daten werden an den Web-Zwischendienst zurückgegeben, der das Ergebnis in JSON zurücksendet und es für die zu aktualisierenden Felder an den Client zurückgibt. Wenn Sie einen Wert ändern, können sich Hunderte anderer ändern. Daher ist es entscheidend, dass wir keine Berechnungen erzwingen, bevor andere abgeschlossen sind. –

+0

Es ist ein Integrationsprojekt - wir haben eine externe Berechnungs-Engine zu einer bestehenden Web-Anwendung hinzugefügt. Diese externe App verarbeitet nur jeweils eine Anfrage, und nur API ist XML. –

+0

Dann könnte eine Warteschlange auf dem Server, die die Anfragen erhalten und die Arbeit in der empfangenen Reihenfolge tun, eine bessere Herangehensweise sein. Die Auflösung in der Benutzeroberfläche sieht hacky aus und ist sicherlich fehleranfällig. – Aleris

0

Ich nehme an, dass Ihr Klickereignis auf dem Ergebnis des Blur-Ereignisses beruht. In diesem Fall denke ich, dass es am besten ist, die Schaltfläche zu deaktivieren (oder sie auszublenden), und sie dann zu aktivieren, wenn die Onblur-Anforderung abgeschlossen ist.

<input type="submit" disabled="disabled" /> 

Und der pseudo-Javascript ...

onblur = function() { 
    //ajax complete 
    mybutton.disabled = false; 
} 
0

Das Problem mit einem festen Timeout von 50 ms ist, dass man auf Zeit angewiesen ist, das Problem zu lösen. Was ist, wenn der Computer aus irgendeinem Grund langsam ist oder wenn Sie auf einem langsamen Netbook oder einem alten Computer oder einem alten Browser oder Handy laufen? Vielleicht solltest du setInterval anstelle von setTimer verwenden und warten, bis du vergewissert hast, dass die Deaktivierung tatsächlich stattgefunden hat.

Verwandte Themen