So I have yet another little JavaScript issue with my current project: JS events aren't firing in the order that I expect them to.
Let's say I've got a text field with an onBlur event that fires an AJAX call that does some calculating on the server (I'm using .NET 3.5's WebServices with JSON). Its callback method populates a page full of text fields with results from the calculation.
I've also got a button with an onClick event that initiates a different calculation, but it relies on having finished the text field's onBlur event(s) before the results of its onClick event will be valid.
I have a simple mutex in place which works fairly well, until the following situation: The user changes a value in one of the text fields but has not yet left it, so focus is still on the text field. While the text field has focus, they click a button that has an onClick event. More often than not, the browser (IE7 in this case) fires the onClick event of the button BEFORE the onBlur event of the text field!
This causes some havoc, and I have no clue how to enforce the order of the events being fired. My little mutex works as follows: when any of the events are fired, I set a flag that means "wait for the callback." Any subsequent events that fire check the flag before doing anything. In the callback a reset the flag to false.
So, any ideas? I guess I almost want to make all of the web service calls synchronous, but I don't know if there's an easy way to do that.
Other things I've tried or things you should know: - Disabling the buttons in the onBlur of the text fields -- their onClicks still fire before they get disabled - Keep in mind, this only happens when editing a text field and immediately clicking a button. If the user clicks on empty space to fire the onBlur of the text field before clicking the button, it all works perfectly.
EDIT:
After some comments and more testing here's what I'd like to be able to do:
In the onBlur of the text field, I want to disable the button so that its onClick event doesn't fire. However, when I code this up, the onBlur event fires, I can see the button getting disabled, but its onClick event is still firing.
Here's some sample code. Sometimes the onClick event happens, and sometimes it doesn't. If you up the setTimeout to 500ms or so, it seems to always prevent it. Maybe this is an 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:
I updated the code I posted to reflect some of the issue, so you can see how I tested this. When you have focus on a text field and then click on a button, the onMouseDown event of the button fires BEFORE the onBlur event of the text field. Because of this, I can set a flag so that I know they tried to click the button. Then in the onSuccess callback event of the AJAX call fired by the onBlur event of the text field, I can see if the button flag is set (so I know they tried to click it) and fire that event (disabling everything else so it can complete). There are only a few buttons on the form that need this behavior, so I think this solution will be sufficient for now.