Emulating activeElement property with DOM browsers

Sometimes you need to know which page element has the focus. Internet Explorer solves the problem nicely with the document.activeElement property, which is unfortunately a proprietary extension and thus not available in other DOM-compliant browsers.

All the solutions I've found with Google were kludges along the lines of “scan all the elements and test each one to see whether it has focus”. It's much better to use the capture phase of the DOM event model and track the focus and blur events with the document object:
function _dom_trackActiveElement(evt) {
  if (evt && evt.target) {
    document.activeElement =
        evt.target == document ? null : evt.target;
  }
}
 
function _dom_trackActiveElementLost(evt) {
  document.activeElement = null; }
 
if (document.addEventListener) {
  document.addEventListener("focus",_dom_trackActiveElement,true);
  document.addEventListener("blur",_dom_trackActiveElementLost,true);
}

This implementation is slightly over-pessimistic; if the browser window loses focus, the activeElement is set to null (as the input control loses focus as well). If your application needs the activeElement value even when the browser window doesn't have the focus, you could remove the blur event listener.

4 comments:

ariel said...

thanks for your post - that was quite helpful. i implemented a variation of this specifically to track keyboard based focus change and to retrigger the blur event in a way that is similar to how they are produced in firefox and in IE.

if ($.browser.safari && document.addEventListener)
{
function fixupActiveElement(e)
{
if (e.type == "keyup")
{
document.activeElement = null;
return;
}
if (!e.target || !$(e.target).is(":input"))
{
return;
}
var oldActiveElement = document.activeElement;
document.activeElement = e.target;
if (e.type == "focus" && oldActiveElement && oldActiveElement != document.activeElement )
{
// retrigger blur with document.activeElement set to the right thing
$(oldActiveElement).trigger("blur");
}
};
// this is the order in which events will fire if we shift focus using a tab
document.addEventListener("keydown", fixupActiveElement, true);
document.addEventListener("focus", fixupActiveElement, true);
document.addEventListener("keyup", fixupActiveElement, true);
}

1001001 said...

The above uses jQuery (http://jquery.com/) to abbreviate document.getElementById with $('#' + id || element) and $.browser

...just for clarity

Rahul said...

But, 'document.activeElement' is read-only. (https://developer.mozilla.org/en/DOM/document.activeElement)

"document.activeElement =
evt.target == document ? null : evt.target;"
throws error.

Ivan Pepelnjak said...

Yeah, the trick only works with those browser versions that don't support native activeElement, so you should try/catch the code that sets it.

Post a Comment