可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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
回答1:
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.
回答2:
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:
- Script sets className;
- Script sets focus;
- Browser throws exception because focused element is hidden;
- Script ends because of error;
- 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:
- Script sets className;
- Script ends;
- Browser updates things based on the change(s) made by the script;
- Timeout fires;
- Timeout script sets focus, which now succeeds.
回答3:
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 :)
回答4:
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.