Thursday, July 24, 2008

Javascript: problem giving focus in onBlur event handler

So, yesterday I was writing a javascript that would allow me to avoid using Ajax. I have a tool where you create a new item and give that item a name. I don't want you to name that item the same as other items of the same type and I don't want to warn you about that AFTER you've already submitted the rest of the related data.

When the page loads, I use Coldfusion to build a javascript array of the item names already in use in your account. This is how I avoid Ajax here.

When you navigate away from the text field where you should enter the item name, I call my function using the onBlur event handler for the text field. My function basically loops over the array of item names already in use in your account. If the array item matches the value in the field, I alert you to this and give focus() and select() to the field I'm alerting you about.

I didn't use the form's onSubmit event handler intentionally. By using onBlur of the field, I can be very persistent about making you create an item name that is not already in use.

So, here is where the trouble started. While testing, every time I typed a name that was already in use, I got the alert, but the focus() didn't seem to work in Firefox. I did some searching online and found others having similar problems in Firefox. Someone suggested this fix/workaround:
setTimeout(function() {fieldObj.focus();}, 0)

I used it. It worked. This found solution saved me from some obsessive thinking through the night.

NOW, today I created a function for another page that brings focus() to a required field using the form's onSubmit event handler. In my function, I just used the plain old objRequiredField.focus() after my alert and the focus() worked perfectly in Firefox. VERY confusing.

Here's my theory--onBlur doesn't finish "blurring" or removing focus from the object until AFTER the event handler is finished doing anything you've added to it. So, yesterday's problematic onBlur function was running just fine, but it would give focus() based on my script orders and then, when my function was done, the event handler would finish its default behavior--it would drop focus on the original object and bring focus to the clicked object. setTimeout() allows the blur/focus event go ahead while my function waits. Then, after that minuscule delay, my focus() works as I intend.

Shwew--I'm glad I didn't figure that out in my sleep last night!!

9 comments:

Unknown said...

@David,

That is interesting. I have not had problems with onblur() that I have noticed. It is possible that that was happening and I just never realized it. Funky.

David said...

I was reviewing my code and started to wonder if my onBlur approach might not work when the user clicks the submit button--would it alert, focus and the just submit the form?

Answer: NO. The onBlur event handler breaks the submit event.

Unknown said...

I have run into problems with OnBlur() not firing if the form was submitted via the user pressing (Enter) when in that field. Not sure if that still happens or that is the expected behavior.

Onblur is an odd beast to play with. For things that are more time sensitive, I will try to use something like onkeyup.

David said...

@Ben,

Thanks for pointing that out. I'd forgotten about that and it DOES still present the same problem--at least in FireFox and probably in IE too I'd bet.

Funny, I've had challenges with the user experience when using onKeyUp! I'd call that one a beast too. I guess I'm going to add my onBlur function to the form's onSubmit function and see how that goes.

This reminds me of a pet peeve. I've got a bunch of pages designed by who-knows-who where the developer used an input type=image or type=button and put the form validation in the onClick of the button. The same problem here, if someone clicks [ENTER] client-side validation is circumvented--numbskull!

David said...

BTW, I'm getting ALL KINDS of weird FireFox browser behavior as I test all of this. I'm hoping it's not the way I loop over my array or anything but every time I test a few times, the whole browser stops letting me click into field (in ANY tab) and stops letting me access the address bar (in some tabs, even unrelated ones) and I have to restart FireFox to get complete control back. Maybe this will uncover some numbskull code bug I've created for myself!

Unknown said...

@David,

It could just be a really long loop which is causing the browsre to feeze up. That would require thousands of records in the array, though (I think).

If you can, you might want to figure out a way to use an object "index" rather than array.

For example:

var objIndex = {};
objIndex[ "foo" ] = SomeValue;
objIndex[ "bar" ] = SomeValue;


// Check to see if form input is valid.
if (objIndex[ formInput.value.toLowerCase() ]){
// The index value was good.
} else {
// The index value was bad.
}

I don't know enough about your use-case to know if that is valid, but I have that to be extremely fast look-ups in the right situation.

David said...

@Ben,

Thanks. I will try that. It looks like a way to start practicing with Javascript Objects which I've been very interested in doing lately.

David said...

So, I did try @Ben's suggestion and used an object "index" rather than an array. Even if my list doesn't get so long that is slows down the when using the array approach, I like the syntax MUCH better:

var objProfileLabels = {};
objProfileLabels["TEST"] = 'TEST';
objProfileLabels["TEST1"] = 'TEST1';
objProfileLabels["TEST2"] = 'TEST2';


function checkProfileName(fieldObj){
if (objProfileLabels[ fieldObj.value.toUpperCase() ]){
// The index value was already in use.
alert('The profile name "' + objProfileLabels[ fieldObj.value.toUpperCase() ] + '" is already in use. Please choose another name for your new profile.');
fieldObj.focus();
fieldObj.select();
return false;
} else {
// The index value was NEW.
return true;
}
}

Unknown said...

@David,

Glad you like it. I'm happy to pass that on as it as helped me many times in both Javascript and ColdFusion.