15 August 2008...4:28 pm

Focus issues in Flex

Jump to Comments

You’ll be familiar with FocusManager and how it manages focus around text input controls, right? Well, once again, the browsers have hit back…

It seems, at least in Internet Explorer 7, that if you hit tab on a text box, it will allow Flex to process the keydown, but then it will merrily move focus off the Flash Player altogether. In other words IE captures the tab key to move around its own controls.  If your Flex app has forms made up of text boxes, that’s something you really don’t want.

The only way I’ve found to get around this is a JavaScript hack in the wrapper HTML.  It basically sets up a listener for keyboard events and passes the tab key down to the Flash Player.

In addition, if your Flex app starts up showing a text box (e.g. a login form) then that won’t get focus.  The reason for this is that Flash Player itself doesn’t automatically get focus.  If you click anywhere on the Flex App, you’ll find that the first textbox gets focus as you might expect.

This little hack fixes both these issues in Internet Explorer.   I tried these fixes in Firefox as well, but although it fixes the issue with tabbing around forms, I just could not make Firefox give Flash Player the focus on startup.

In the wrapper HTML, I’ve added some extra JavaScript – setupKeyListener() is executed on load, and it sets up the listener, and then tries to set focus to the Flash Player. Note that I’ve named the object mainApp – this has to match the id field passed to the AC_FL_RunContent() function, or the id of the <object> tag.

<script language="Javascript" type="text/javascript">
function setupKeyListener()
{
    // Add the event handler detectEvent on the keydown event
    document.onkeydown = detectEvent;
    if (isIE)
   {
       // force the focus
       var flashPlayer = document.mainApp || window.mainApp;

       flashPlayer.focus();
    }
    else
    {
        // For Firefox, it's just not worth bothering...it won't work.
    }
}

// this is the detectEvent event handler
function detectEvent(e)
{
    var evt = e || window.event;

    // check for the TAB key
    if (evt.keyCode == 9)
    {
        // call the actionscript function to pass it the SHIFT key pressed
        mainApp.handleExternalTabKey(evt.shiftKey);

        // prevent the browser the default actions for the key pressing
        return false;

    }
    else
    {
        return true;
    }
}
</script>

<body scroll="no" onload="setupKeyListener()">
...etc

In your main application class, you’ll need to export the handleExternalTabKey() function something like this:

private function onCreationComplete(event:FlexEvent):void
{
    ...stuff...

    ExternalInterface.addCallback("handleExternalTabKey", handleExternalTabKey);
}

private function handleExternalTabKey(name:String):void
{
    if (name == "false")
    {
        // only the TAB key has been pressed
        focusManager.getNextFocusManagerComponent().setFocus();
    }
    else
    {
        // the combination of  SHIFT+TAB has been pressed
        focusManager.getNextFocusManagerComponent(true).setFocus();
    }
}

Update

In response to Daniel’s comment below, I had another look at the issue with setting focus to the Flex app at startup in Firefox. I’m happy to say that I found a solution that seems to work at least on our app. Here’s the new version of setupKeyListener()…


function setupKeyListener()
{
    // Add the event handler detectEvent on the keydown event
    document.onkeydown = detectEvent;
    setTimeout('mainApp.focus()', 100);
}

It seems that in IE you can go right ahead and set focus to the Flash instance and it works fine. But it occurred to me that in Firefox the Flash instance might not have loaded fully at the point I called focus(). So just for a lark I put the focus() call on the end of a timer, and it seems to work. I’ve just got an arbitrary value of 100ms in the code above. I guess it could be anything because all I’ve really done is allow control to return from the setupKeyListener() function so that Firefox can finish whatever it’s doing and then call the focus() at some later time. Incidentally, the code above now seems to work for IE and Firefox.

Hopefully this is some use (particularly to Daniel) and I’m certainly going to add this fix to a bunch of our apps.

Thanks to Daniel for prompting me to have another look at this…

5 Comments

  • you write “I tried these fixes in Firefox as well, but although it fixes the issue with tabbing around forms, I just could not make Firefox give Flash Player the focus on startup”

    were you able to find a sln to this?

  • Hi Daniel,

    In response to your comment I had another look at the Firefox thing. It must have been the fact that I was taking a totally fresh look at this problem as I tried something different and it seemed to work. I’ve updated the post with my solution.

    Thanks for commenting.

  • I have a minor question regarding this very good solution.
    Since we are adding a javascript into the HTML wrapper, is it not possible that the browser does not executes the javascript depending upon the browser settings.

  • Hi Dan,

    Yes I think most, if not all, browsers can be set to ignore any javascript. However, if someone did actually do this, I suspect that most of the websites on the planet would not run properly because they would need at least some javascript to provide the functionality. So I don’t believe it would reasonable for a user of your website to switch off javascript and expect either yours or anybody else’s web apps to run. For that reason, we just take it for granted that the javascript will run.

    I hope that answers your question…..

    Cheers,

    Ed.

  • Thank you for a very helpful post. In my testing I came up with a variation that may be a bit easier to maintain, as it embeds the JavaScript code in the Flex app so there is no need to modify index.template.html.

    I added the following to my application tag:
    creationComplete=”ExternalInterface.call(‘function(){window.document.’+this.id+’.focus();}’)”

    By having Flex call the JavaScript once the application finishes loading, there is no need for a timer delay. It worked for me in both IE and Firefox.


Leave a Reply