views:

268

answers:

4

I have an HTML along the following lines:

<div class="hiddenClass"> // this implies display:none
 <span>
    <input type="text" id="hiddenInput"/>
 </span>
</div>

and a Javascript event (triggered in a "succes" method of an jQuery $.ajax() call ), that needs to make this div visible and then set the focus to the control. Something like:

 this.DOMElements.divElement.className="showClass"; //a CSS class with display:block;
 this.DOMElements.hiddenInputElement.focus();
 this.DOMElements.hiddenInputElement.select();

strange enough, this code only works part of the time. In some cases (only sometimes!!) the focus/select commands generate warnings about focusing/selecting an invisible control. The control will be made visible, but the focus is not moved, nor is the text selected.

I found (somewhat) of a solution by moving the focus/select code in a separate function and delay-call it by means of a

this.DOMElements.divElement.className="showClass"; //a CSS class with display:block;
setTimeout("focusinput('hidddenInput')",1);

Ok, finally my question: since javascript is single-threaded.. how come there is a delay between the time I made the parent div visible, and the time I can set the focus/select on the child input element ? How could this be a race condition?

Edit: Happens in IE8

A: 

I'm not sure what's going on here, but it might not be a bad idea to grab a reference to the div your using, store it in a variable, and then operate on that, instead of grabbing it with getElementByID() each time.

So you're above code would be:

 var myDiv = document.getElementById("hiddenInput");
 if(myDiv)
 {
    myDiv.className="showClass"; //a CSS class with display:block;
    myDiv.focus();
    myDiv.select();
 }

Also: There are certain javascript functions where the browser is allowed to spin up another thead (for instance, the load() function); just something to keep in mind :)

wyrmmage
Actually, I have both the div and the input elements as members in my class, I was just simplyfing things in my example. If you think this is relevant, I'll go ahead and change the example
Radu094
I don't know. It might be.Have you tried changing the style directly instead of changing the className? I'm still not sure how this could be a race condition, but it definitely seems like one.
wyrmmage
A: 

If you're using jQuery, use this to display and set focus:

$(".hiddenClass").fadeIn("fast", 
    function() {
        $("#hiddenInput").focus();
    }
);

or

$(".hiddenClass").show(0, 
    function() {
        $("#hiddenInput").focus();
    }
);

If you want to show it without any fade in.

Basically it's fading the hidden div in (you can replace .hiddenClass with an id if you one want a particular div to be shown and not all elements with .hiddenClass), and once it's done that it executes the callback function to give focus to the input.

That way you it will not try to give the input focus until after the div has been fully shown.

Matt
Tested this, and it works. But I believe the jQuery fadeIn/show is also using a setTimeout underneath, so the question still stands as to why is there a race condition?
Radu094
A: 

I've had this happen to me before. In most cases, I found that by setting a timeout after the setting the display fixes the issue in IE. Unfortunately, it isn't the cleanest fix.

Dhana
+1  A: 

Ok, finally my question: since javascript is single-threaded.. how come there is a delay between the time I made the parent div visible, and the time I can set the focus/select on the child input element ?

You just answered your own question: this happens because JS is single-threaded; that is, it blocks the browser from updating its rendering until the script has finished executing.

So when you execute the code:

divElement.className="showClass";

the className property of the element has been updated, but as your script is still executing, the element is not immediately redrawn as visible. Therefore, when you execute

hiddenInputElement.focus();

the element is still hidden and you get the error.

So, in your first version, the sequence of execution in the single thread is:

  1. Script sets className;
  2. Script sets focus;
  3. Browser throws exception because focused element is hidden;
  4. Script ends because of error;
  5. Browser updates things based on the change(s) made by the script up to the point of failure.

When you use the setTimeout approach, the sequence is:

  1. Script sets className;
  2. Script ends;
  3. Browser updates things based on the change(s) made by the script;
  4. Timeout fires;
  5. Timeout script sets focus, which now succeeds.
NickFitz
If what you say is true then the original code should always fail, but it works sometimes, so this cannot be it.
Radu094