15

I've got a little issue with the focus event that I just became aware of. Apparently, focus fires when switching to another browser tab and then back again. I'd rather not have that happen; is it possible?

I was never aware of this until today. Here's a little demo: http://jsfiddle.net/MJ6qb/1/

var times = 0;
$('input').on('focus', function() {
    times ++;
    $(this).after('<br>Focused '+times+' times');   
});

To reproduce: Focus on the input, then switch browser tabs, then switch back. All browsers seem to fire the focus event when you switch back to the tab, and Google Chrome 19 is firing it twice!

Ideally, the function should not run when switching browser tabs at all but only on user click or Tab, but now that I'm aware of the Chrome issue I'm a bit more concerned about that because it's resulting in extra unwanted back-to-back AJAX requests in my real app (it's for fetching results for an autocomplete that needs to be up to date, but not so much that I want to use the keyup event).

It doesn't seem jQuery related (I did test with vanilla javascript) but I can using jQuery for a solution. Is there anything I can do about this? I know I can use jQuery's one() but I do want the function to run more than once.

Wesley Murch
  • 101,186
  • 37
  • 194
  • 228
  • 1
    Slightly unrelated, but you might be able to detect when the browser tab is focused or blurred and override the default behavior like in [this](http://stackoverflow.com/a/6184276/1028949). The Chrome double-fire is a [known bug](http://code.google.com/p/chromium/issues/detail?id=124583), from what I can tell. – jeffjenx May 18 '12 at 17:41
  • Ah thanks for that, didn't realize it was a known bug, only found it while testing the code in Chrome just before posting to make sure it wasn't a FF thing (which I develop in). I kind of feel like giving up and "letting it ride" but it's really annoying. I'll see if I can do something with the code in that answer. – Wesley Murch May 18 '12 at 17:45
  • 1
    The behavior you are experiencing is expected. When you leave the tab, you are unfocusing that textbox and when you return you are focusing it again. The same goes for leaving to another application. Unfortunately, I cannot think of a way to override or get around this. – jeffjenx May 18 '12 at 17:46
  • As long as the blur event fires as well (which seems to be the case), I guess that makes sense - I just never realized it. – Wesley Murch May 18 '12 at 17:57

2 Answers2

9

Try this

var times = 0;
var prevActiveElement;

    $( window ).on( "blur", function(e){
            prevActiveElement = document.activeElement;
    });

    $('input').on('focus', function() {
        if (document.activeElement === prevActiveElement) {
            return;
        }
        prevActiveElement = document.activeElement;
        times++;
        $(this).after('<br>Focused ' + times + ' times');
    }).on( "blur", function(){
        prevActiveElement = null;  
    });​
Esailija
  • 138,174
  • 23
  • 272
  • 326
  • Looks like this works! Thanks for the workaround! Also never knew about `activeElement` so double thanks for that piece of info. – Wesley Murch May 18 '12 at 18:00
  • By the way, this is working perfectly in production too with multiple inputs: http://jsfiddle.net/MJ6qb/5/ (if you're curious, open the console to see the requests). – Wesley Murch May 18 '12 at 18:16
  • @WesleyMurch the `$("body").on("blur")` isn't triggered at all. `Blur` doesn't bubble, you probably want `$("body").on( "focusout", "[data-autocomplete]", fn)`. The equivalent for focus is `$("body").on( "focusin", "[data-autocomplete]", fn)` – Esailija May 18 '12 at 18:25
  • Ah thanks for spotting that, I attached the blur event to the wrong object. – Wesley Murch May 18 '12 at 18:41
  • Still getting used to `on` over `live`. All right, fixed with chaining `.on('focusout', function(){ prevActiveElement = null; });` Wonderful, thank you. – Wesley Murch May 18 '12 at 18:53
3

Try this to get around the problem:

var times = 0, foc=true;

$(window).on('focus', function() {
    foc = false;
    setTimeout(function() {foc=true}, 200);
});

$('input').on('focus', function() {
    if (foc || times===0) {
        times ++;
        $(this).after('<br>Focused '+times+' times');   
    }
});

FIDDLE

adeneo
  • 312,895
  • 29
  • 395
  • 388
  • So far, doesn't fire on click unless you focus first with another method like TAB (or clicking another input first) - still testing. But yes, interesting solution! – Wesley Murch May 18 '12 at 17:51
  • That's strange, seems to be working fine here in Chrome, the focus event fires every time, but not on browser tab changes. – adeneo May 18 '12 at 17:57
  • It just doesn't seem to work if you click into the input before doing anything else. @adeno Did you try that? – Wesley Murch May 18 '12 at 17:58
  • Yeah, same here in Firefox. I must first "focus" into the window before focusing into the input field in order to trigger the event. – jeffjenx May 18 '12 at 17:59
  • Ah, I see, did'nt try that for some reason, always clicked the window first, but updated the answer anyway, should work now! – adeneo May 18 '12 at 18:03
  • Yep looks like it works now. I would absolutely use this if it weren't for the other answer. `setTimeout` is a nice trick but seems about 1% hacky. Thank you very much! – Wesley Murch May 18 '12 at 18:21