Joel Purra
Joel Purra

Reputation: 25127

$(":focus") returns no element for <input type="file" /> in Firefox

I can't seem to get the currently focused/active element as a jQuery object in Firefox, if it is an <input type="file" />. It works on other input types (text, password, submit, others) and on other element types (<select>, <textarea>, others).

HTML

<input type="file" />

Javascript

// Cannot find input type file elements with :focus,
// $focused.length is always 0 in Firefox (tested using FF 10.0.1)
var $focusedTest1 = $(':focus');

// This line throws for Firefox (tested using FF 10.0.1)
// Permission denied to access property 'nodeType'
// @ http://code.jquery.com/jquery-1.7.1.js:108
// in jQuery.fn.init, below "Handle $(DOMElement)"
var $focusedTest2 = $(document.activeElement);

Steps to reproduce

  1. Use Firefox.
  2. Focus the file box:
    • press tab until you reach it
    • or click in it.
  3. While focusing the file box, try to get a result from $(':focus').

See jsFiddle demonstration of getting the id of the focused element - test it with Firefox.

Does anyone have a solution for getting the focused/active element as a jQuery object that works for <input type="file" />?

The solution needs to be fully generic as the functionality is part of a plugin. I will not have control over the page the script will run on.

Upvotes: 3

Views: 3722

Answers (3)

Joel Purra
Joel Purra

Reputation: 25127

Edit: This solution has been implemented where the problem was first found, in EmulateTab. See getFocusedElement().


Found a solution myself, after a break from coding - but it is not a very clean solution. It is basically the same solution suggested by @Neil while I first wrote this post.

Try the updated jsFiddle version with focus listeners and try-catch logic in Firefox. It combines :focus, document.activeElement and document level focus listeners that keep track of the last "known" focused element.

Function to find focused element

// Comined function to get the focused element trying as long as possible.
// Extra work done trying to avoid problems with security features around
// <input type="file" /> in Firefox (tested using 10.0.1).
function getFocused() {
    // Try the well-known, recommended method first.
    var $focused = $(':focus');

    if ($focused.size() === 0) {
        try {
            // Fall back to a fast method that might fail.
            // Known to fail for Firefox (tested using 10.0.1) with
            // Permission denied to access property 'nodeType'.
            $focused = $(document.activeElement)
        }
        catch (error1) {
                warnToConsole("Could not use document.activeElement", document.activeElement, error1);

            if (lastFocusedElement !== null) {
                try {
                    // As a last resort, use the last known focused element.
                    // Has not been tested enough to be sure it works as expected.
                    $focused = $(lastFocusedElement);
                } catch (error3) {
                    warnToConsole("Could not use lastFocusedElement ", lastFocusedElement, error3);
                }
            }
        }
    }

    return $focused;
}

Focus listeners

// Keep a reference to the last focused element, use as a last resort.
var lastFocusedElement = null;

function focusInElement(event) {
    lastFocusedElement = event.target;
}

function focusOutElement(event) {
    lastFocusedElement = null;
}

// Start listeners.
$(function() {
    // Start listeners that keep track of the last focused element.
    $(document).on("focusin", focusInElement);
    $(document).on("focusout", focusOutElement);
});

I don't like this solution very much, as it is far from as clean as just a one-line $(':focus'). Other answers are welcome!

Upvotes: 3

sarghir
sarghir

Reputation: 144

Try this - tested in Firefox 10.0.1

$(document).ready(function () {
        var crtFocus;
        $("#id1").focus(function () {
            crtFocus = $(this);
        });
        $("#id2").focus(function () {
            crtFocus = $(this);
        });
        $("#id3").focus(function () {
            crtFocus = $(this);
        });

        $("#click").click(function () {
            // $(crtFocus) contains the currently focused element
            //alert($(crtFocus));
        });
    });


...



<input type="file" id="id1" />
<input type="file" id="id2" />
<input type="file" id="id3" />    
<input type="button" id="click"/>

EDIT - we can use only one selector for all input[type=file] elements instead of one selector per element

$(':input[type=file]').focus(function () {
            crtFocus = $(this);
        });

Upvotes: 0

Neil
Neil

Reputation: 55402

The only way I can think of is to add a capturing focus listener at the window level in which you update a global variable with the event target. (In Firefox, the event target for a focus event in a file input is the file input itself even though document.activeElement returns the "anonymous" button.)

Upvotes: 1

Related Questions