606

On the front page of a site I am building, several <div>s use the CSS :hover pseudo-class to add a border when the mouse is over them. One of the <div>s contains a <form> which, using jQuery, will keep the border if an input within it has focus. This works perfectly except that IE6 does not support :hover on any elements other than <a>s. So, for this browser only we are using jQuery to mimic CSS :hover using the $(#element).hover() method. The only problem is, now that jQuery handles both the form focus() and hover(), when an input has focus then the user moves the mouse in and out, the border goes away.

I was thinking we could use some kind of conditional to stop this behavior. For instance, if we tested on mouse out if any of the inputs had focus, we could stop the border from going away. AFAIK, there is no :focus selector in jQuery, so I'm not sure how to make this happen. Any ideas?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
bloudermilk
  • 17,820
  • 15
  • 68
  • 98
  • 1
    can you try to boil that down to a few sentences of what is required, and what i happening? – mkoryak Jun 08 '09 at 21:28
  • I think you need to look into capturing the focus and blur events (http://docs.jquery.com/Events/focus and http://docs.jquery.com/Events/blur) – Wolfr Jun 08 '09 at 21:34
  • I posted an answer on the related question ["Is there a ‘has focus’ in JavaScript (or jQuery)?"](http://stackoverflow.com/questions/2683742/is-there-a-has-focus-in-javascript-or-jquery/3478393#3478393). I've enhanced [cletus approach cited by gnarf](#2684561). – luistamawong Aug 13 '10 at 15:36

15 Answers15

1007

jQuery 1.6+

jQuery added a :focus selector so we no longer need to add it ourselves. Just use $("..").is(":focus")

jQuery 1.5 and below

Edit: As times change, we find better methods for testing focus, the new favorite is this gist from Ben Alman:

jQuery.expr[':'].focus = function( elem ) {
  return elem === document.activeElement && ( elem.type || elem.href );
};

Quoted from Mathias Bynens here:

Note that the (elem.type || elem.href) test was added to filter out false positives like body. This way, we make sure to filter out all elements except form controls and hyperlinks.

You're defining a new selector. See Plugins/Authoring. Then you can do:

if ($("...").is(":focus")) {
  ...
}

or:

$("input:focus").doStuff();

Any jQuery

If you just want to figure out which element has focus, you can use

$(document.activeElement)

If you aren't sure if the version will be 1.6 or lower, you can add the :focus selector if it is missing:

(function ( $ ) {
    var filters = $.expr[":"];
    if ( !filters.focus ) { 
        filters.focus = function( elem ) {
           return elem === document.activeElement && ( elem.type || elem.href );
        };
    }
})( jQuery );
Community
  • 1
  • 1
gnarf
  • 105,192
  • 25
  • 127
  • 161
  • 4
    This can cause false positives. See http://stackoverflow.com/questions/967096/using-jquery-to-test-if-an-input-has-focus/5391608#5391608 for more information (thanks to Ben Alman and Diego Perini). – Mathias Bynens Mar 22 '11 at 13:01
  • @Mathias Bynens - Thanks for the update (you're more than welcome to edit this community wiki answer btw!) – gnarf Mar 22 '11 at 21:31
  • What about when you need it to work with both 1.5- and 1.6? I don't want to override jQuery's own focus selector. Something like `if (!jQuery.expr[':'].focus) { /* gist from Ben Alman */ }` ? – Betamos Jun 03 '11 at 19:11
  • @betamos - Exactly right... I'll add something into the answer to reflect that. – gnarf Jun 03 '11 at 20:51
  • 2
    @Alex - Please provide a reduced test case on jsFiddle showing the problem, because as far as I can tell, there is no reason this *should not* work on "dynamic" elements. I call shenanigans... – gnarf Jun 14 '12 at 22:14
  • I could be wrong but it wasn't working for me and just wanted to share...I will have to do some more testing to make sure it's not just me.. – Alex Jun 14 '12 at 22:40
  • Can also avoid a focused element in a selector like so: `$('[placeholder]:not(:focus)')` – jocull Feb 13 '13 at 17:43
  • FYI if you're using karma you need to add the element to the body first in your test. See http://stackoverflow.com/questions/19994424/karma-test-cases-focus-event – rouan May 21 '14 at 10:23
48

CSS:

.focus {
    border-color:red;
}

JQuery:

  $(document).ready(function() {

    $('input').blur(function() {
        $('input').removeClass("focus");
      })
      .focus(function() {
        $(this).addClass("focus")
      });
  });
jbutler483
  • 24,074
  • 9
  • 92
  • 145
Daniel Moura
  • 7,816
  • 5
  • 35
  • 60
22

Here’s a more robust answer than the currently accepted one:

jQuery.expr[':'].focus = function(elem) {
  return elem === document.activeElement && (elem.type || elem.href);
};

Note that the (elem.type || elem.href) test was added to filter out false positives like body. This way, we make sure to filter out all elements except form controls and hyperlinks.

(Taken from this gist by Ben Alman.)

Mathias Bynens
  • 144,855
  • 52
  • 216
  • 248
18

April 2015 Update

Since this question has been around a while, and some new conventions have come into play, I feel that I should mention the .live method has been depreciated.

In its place, the .on method has now been introduced.

Their documentation is quite useful in explaining how it works;

The .on() method attaches event handlers to the currently selected set of elements in the jQuery object. As of jQuery 1.7, the .on() method provides all functionality required for attaching event handlers. For help in converting from older jQuery event methods, see .bind(), .delegate(), and .live().

So, in order for you to target the 'input focused' event, you can use this in a script. Something like:

$('input').on("focus", function(){
   //do some stuff
});

This is quite robust and even allows you to use the TAB key as well.

jbutler483
  • 24,074
  • 9
  • 92
  • 145
2

I'm not entirely sure what you're after but this sounds like it can be achieved by storing the state of the input elements (or the div?) as a variable:

$('div').each(function(){

    var childInputHasFocus = false;

    $(this).hover(function(){
        if (childInputHasFocus) {
            // do something
        } else { }
    }, function() {
        if (childInputHasFocus) {
            // do something
        } else { }
    });

    $('input', this)
        .focus(function(){
            childInputHasFocus = true;
        })
        .blur(function(){
            childInputHasFocus = false;
        });
});
James
  • 109,676
  • 31
  • 162
  • 175
1

if anyone cares there is a much better way to capture focus now, $(foo).focus(...)

http://api.jquery.com/focus/

CrackSmoker9000
  • 319
  • 4
  • 13
1

Have you thought about using mouseOver and mouseOut to simulate this. Also look into mouseEnter and mouseLeave

Akhil
  • 271
  • 1
  • 3
  • 11
mkoryak
  • 57,086
  • 61
  • 201
  • 257
  • That is essentially what I am doing with jQuery's hover. You supply two functions, one for mouse in and one for mouse out. – bloudermilk Jun 08 '09 at 21:30
1

An alternative to using classes to mark the state of an element is the internal data store functionality.

P.S.: You are able to store booleans and whatever you desire using the data() function. It's not just about strings :)

$("...").mouseover(function ()
{
    // store state on element
}).mouseout(function ()
{
    // remove stored state on element
});

And then it's just a matter of accessing the state of elements.

cllpse
  • 21,396
  • 37
  • 131
  • 170
0

There is a plugin to check if an element is focused: http://plugins.jquery.com/project/focused

$('input').each(function(){
   if ($(this) == $.focused()) {
      $(this).addClass('focused');
   }
})
Murat Çorlu
  • 8,207
  • 5
  • 53
  • 78
0

I had a .live("focus") event set to select() (highlight) the contents of a text input so that the user wouldn't have to select it before typing a new value.

$(formObj).select();

Because of quirks between different browsers, the select would sometimes be superseded by the click that caused it, and it would deselect the contents right after in favor of placing the cursor within the text field (worked mostly ok in FF but failed in IE)

I thought I could solve this by putting a slight delay on the select...

setTimeout(function(){$(formObj).select();},200);

This worked fine and the select would persist, but a funny problem arose.. If you tabbed from one field to the next, the focus would switch to the next field before the select took place. Since select steals focus, the focus would then go back and trigger a new "focus" event. This ended up in a cascade of input selects dancing all over the screen.

A workable solution would be to check that the field still has focus before executing the select(), but as mentioned, there's no simple way to check... I ended up just dispensing with the whole auto highlight, rather than turning what should be a single jQuery select() call into a huge function laden with subroutines...

Brian
  • 36
  • 2
0

Keep track of both states (hovered, focused) as true/false flags, and whenever one changes, run a function that removes border if both are false, otherwise shows border.

So: onfocus sets focused = true, onblur sets focused = false. onmouseover sets hovered = true, onmouseout sets hovered = false. After each of these events run a function that adds/removes border.

Blixt
  • 49,547
  • 13
  • 120
  • 153
0

As far as I know, you can't ask the browser if any input on the screen has focus, you have to set up some sort of focus tracking.

I usually have a variable called "noFocus" and set it to true. Then I add a focus event to all inputs that makes noFocus false. Then I add a blur event to all inputs that set noFocus back to true.

I have a MooTools class that handles this quite easily, I'm sure you could create a jquery plugin to do the same.

Once that's created, you could do check noFocus before doing any border swapping.

Ryan Florence
  • 13,361
  • 9
  • 46
  • 63
0

There is no :focus, but there is :selected http://docs.jquery.com/Selectors/selected

but if you want to change how things look based on what is selected you should probably be working with the blur events.

http://docs.jquery.com/Events/blur

Chris Brandsma
  • 11,666
  • 5
  • 47
  • 58
0

What I wound up doing is creating an arbitrary class called .elementhasfocus which is added and removed within the jQuery focus() function. When the hover() function runs on mouse out, it checks for .elementhasfocus:

if(!$("#quotebox").is(".boxhasfocus")) $(this).removeClass("box_border");

So if it doesn't have that class (read: no elements within the div have focus) the border is removed. Otherwise, nothing happens.

Drew Gaynor
  • 8,292
  • 5
  • 40
  • 53
bloudermilk
  • 17,820
  • 15
  • 68
  • 98
-2

Simple

 <input type="text" /> 



 <script>
     $("input").focusin(function() {

    alert("I am in Focus");

     });
 </script>
Code Spy
  • 9,626
  • 4
  • 66
  • 46
  • That does not tell you which element is currently in focus. It is called when a new element gets the focus. So if an element already has the focus and you want to know whether 'this' is the one, this code is useless. – Alexis Wilke Feb 01 '14 at 00:46