tags:

views:

16713

answers:

6

I would like to find out, in Javascript, which element currently has focus. I've been looking through the DOM and haven't found what I need, yet. Is there a way to do this, and how?

Edit: The reason I was looking for this:

What I'm trying to do is make keys like the arrows and enter navigate through a table of input elements. Tab works now, but enter and arrows do not by default it seems. I've got the key handling part set up but now I need to figure out how to move the focus over in the event handling functions.

+24  A: 

If you're trying to find out which form field has focus, you can't. The best you can do is add a "focus" event handler to all fields, and record the last-focused field in a variable somewhere. You can also add a "blur" handler, and clear the variable if you get a blur event on the last-focused field.

EDIT: Thanks to Gregers for pointing out document.activeElement now works in all major browsers. So you can use that instead.

JW
Things have changed since the question was answered. Safari 4 was released yesterday, with support for document.activeElement property. So now it's supported in the latest release of all major browsers (IE,FF,Safari,Chrome,Opera). I'd only use the event hack as a fallback for older browsers:if(!document.activeElement) { /* add event-listeners to set document.activeElement for older browsers */ }
gregers
If no element has been focused, what does document.activeElement return? Can we rely on it to be consistent between those browsers that support it?
Stewart
Not sure about IE, but FF and Safari both return the BODY element.
JW
+25  A: 

As said by JW, you can't find the current focused element, at least in a browser-independent way. But if your app is IE only (some are...), you can find it the following way :

document.activeElement
Wookai
FF3 too. This is actually part of the HTML5 spec around "focus management".
Crescent Fresh
Doesn't work in Chrome. :(
Brad8118
It works in the current version of Chrome and Opera (9.62). Does not work in Safari 3.2.3 on OS X, but it works in Safari 4 that was released yesterday :)
gregers
A: 

Untested, but you might consider using a CSS selector framework built into a javascript library. For example, with Dojo/sizzle:

var elements = dojo.query('*:focus');
if (elements) {
    //process focused-elements here
}

For better performance, you can qualify the scope and element. For example, to look for the focused input within form with id frm1:

var elements = dojo.query('input:focus', dojo.byId('frm1'));
Mike Griffith
I don't believe that dojo or jQuery/Sizzle support the focus pseudo-class... Have you seen this documented anywhere?
Prestaul
Dojo doesn't support :focus either.
scotts
+1  A: 

Just putting this here to give the solution I eventually came up with.

I created a property called document.activeInputArea, and used jQuery's HotKeys addon to trap keyboard events for arrow keys, tab and enter, and I created an event handler for clicking into input elements.

Then I adjusted the activeInputArea every time focus changed, so I could use that property to find out where I was.

It's easy to screw this up though, because if you have a bug in the system and focus isn't where you think it is, then its very hard to restore the correct focus.

Tony Peterson
+2  A: 

A little helper that I´ve used for these purposes in Mootools:

FocusTracker = {
    startFocusTracking: function() {
       this.store('hasFocus', false);
       this.addEvent('focus', function() { this.store('hasFocus', true); });
       this.addEvent('blur', function() { this.store('hasFocus', false); });
    },

    hasFocus: function() {
       return this.retrieve('hasFocus');
    }
}

Element.implement(FocusTracker);

This way you can check if element has focus with el.hasFocus(); provided that startFocusTracking() has been called on the given element.

Joel S
A: 

I liked the approach used by Joel S, but I also love the simplicity of document.activeElement. I used jQuery and combined the two. Older browsers that don't support document.activeElement will use jQuery.data() to store the value of 'hasFocus'. Newer browsers will use document.activeElement. I assume that document.activeElement will have better performance.

(function($) {
var settings;
$.fn.focusTracker = function(options) {
    settings = $.extend({}, $.focusTracker.defaults, options);

    if (!document.activeElement) {
        this.each(function() {
            var $this = $(this).data('hasFocus', false);

            $this.focus(function(event) {
                $this.data('hasFocus', true);
            });
            $this.blur(function(event) {
                $this.data('hasFocus', false);
            });
        });
    }
    return this;
};

$.fn.hasFocus = function() {
    if (this.length === 0) { return false; }
    if (document.activeElement) {
        return this.get(0) === document.activeElement;
    }
    return this.data('hasFocus');
};

$.focusTracker = {
    defaults: {
        context: 'body'
    },
    focusedElement: function(context) {
        var focused;
        if (!context) { context = settings.context; }
        if (document.activeElement) {
            if ($(document.activeElement).closest(context).length > 0) {
                focused = document.activeElement;
            }
        } else {
            $(':visible:enabled', context).each(function() {
                if ($(this).data('hasFocus')) {
                    focused = this;
                    return false;
                }
            });
        }
        return $(focused);
    }
};
})(jQuery);
Jason