views:

2625

answers:

3

This may be the most obscure bug I have yet encountered in many years of working with JavaScript and any version of Internet Explorer. We're using YUI 2.7 for some of the (non)convenience methods. Sigh, what I would do for jQuery....

This is affecting Internet Explorer 6 and Internet Explorer7. Internet Explorer 8 behaves properly. All other browsers also behave properly.

Problem: When I set focus on a particular element, I get the following error:

Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept focus.

So I have a div called 'add-comment-login-overlay' that contains the input element. This div is display:none until the user clicks one of several links called 'login'.

The following is the JavaScript code I'm using that selects the 'login' links, which moves the position of the 'add-comment-login-overlay' in the DOM, sets display:block, then sets focus on the 1st input field in the overlay. It's this process of setting focus that is causing the error I wrote above.

//Get Login links in comment forms.
links = YAHOO.util.Dom.getElementsByClassName('addCommentLoginLink');

//Set click event for login links.
YAHOO.util.Event.addListener(links, "click", function(el){

    //Stop link.
    YAHOO.util.Event.preventDefault(el);

    //Move login overlay in DOM.
    if( el.srcElement ){
        var target = el.srcElement;
    }else{
        var target = el.currentTarget;
    }

    YAHOO.util.Dom.insertAfter( overlay, target.parentNode.parentNode.parentNode.parentNode );

    //Set to visible.
    YAHOO.util.Dom.setStyle( overlay,'display', 'block' );

    //Set focus to username field.
    document.getElementById('add-comment-login-overlay-username-input').focus();
});

I can absolutely confirm that the overlay div is completely visible. I can look at it. I have added a setInterval timer to measure what's happening and it has no effect. At one point I had 10 seconds going between the time the overlay was visible and when focus() was called and the error still occurs. Other than the error, the form is completely functional other than this error.

This is a simplified version of the overlay HTML code as a reference.

<div id="add-comment-login-overlay" class="add-comment-login-overlay" style="display: block;">
    <div class="add-comment-login-overlay-content clearfix">
        <div class="add-comment-login-overlay-signin clearfix">
            <input type="text" tabindex="2001" id="add-comment-login-overlay-username-input"/>
            <input type="password" tabindex="2002" id="add-comment-login-overlay-password-input"/>
        </div>
    </div>
</div>
A: 

Set the focus in a try/catch block.

lod3n
Unfortunately I'm not trying to put a piece of gum in the leaky dam to patch it over. I'd like to find out what the heck is causing this.
Geuis
Well, in my opinion, a piece of gum in ie6 is the best practice. This browser is full of strange bugs that you don't try to understand them, you just try do make your app work. I haven't tested Iod3n approach, but if it works, if it doesn't hurt performance at all, you should patch your app an go out for a beer.
GmonC
A: 

At a guess, I'd say that the insertAfter call is confusing the IE DOM. Can you test the code without the insertAfter call? Does it fail in the same way? If not, is there another way you could achieve the same result? Maybe copy the nodes and delete the originals, instead of moving the nodes?

Alohci
+3  A: 

This is an old as heck bug in IE (glad to know it's fixed in 8). I don't know the official cause, but I believe it has to do with IE not repainting the DOM until after the execution context is complete, meanwhile trying to focus() the element while it thinks it's still hidden:

function calledAtSomePoint() { // begin execution

    // ...

    element.style.display = ''; // show container
    input.focus(); // IE thinks element is hidden 

    // end of execution, IE repaints the DOM but it's too late
}

The solution is to use setTimeout:

setTimeout(function() {
    document.getElementById('add-comment-login-overlay-username-input').focus()
}, 0)

I've had it happen many-a-time, including with jQuery. It's no fault of any library. The setTimeout has always worked around it for me.

Crescent Fresh