17

This has been asked here before, but several years ago, and there was no cross-platform solution at the time (other than the setTimeout solution, which is really not very handy).

I'd like to do onblur="foo(parm);" and have foo be able to determine which element now has focus.

I'm using regular javascript; no jQuery for this one, please.

Is that possible these days?

Community
  • 1
  • 1
Jonathan M
  • 17,145
  • 9
  • 58
  • 91
  • Well it is doable in jQuery, which means it's doable in Javascript. I have no idea though, good question. +1 – Madara's Ghost Aug 17 '11 at 16:19
  • Hmmm. Can you post the jQuery in an answer and maybe I can work backwards from that? – Jonathan M Aug 17 '11 at 16:20
  • possible duplicate of [When onblur occurs, how can I find out which element focus went *to*?](http://stackoverflow.com/questions/121499/when-onblur-occurs-how-can-i-find-out-which-element-focus-went-to) – Daniel A. White Aug 17 '11 at 16:22
  • 3
    @Daniel: Yes, I mentioned that in the post, but the answers at the time were not cross-platform. – Jonathan M Aug 17 '11 at 16:22
  • @Daniel: I asking if the technology has changed, if there's an answer today. Please remove the close vote. Please... :) – Jonathan M Aug 17 '11 at 16:24
  • 2
    There's nothing not handy about `setTimeout`. It's the best solution, and the only one which actually works. All the others fail because the "active element" when the blur event is fired is not yet the one that was clicked or tabbed to: it will only receive the focus following another event, when the blur event's processing's done! – Tom Jul 22 '12 at 21:12

5 Answers5

7

You can try something like this:

function whereDidYouGo() {
    var all = document.getElementsByTagName('*');

        for (var i = 0; i < all.length; i++)
            if (all[i] === all[i].ownerDocument.activeElement)
                return all[i];
}

EDIT:

function whereDidYouGo() { return document.activeElement; }
qwertymk
  • 34,200
  • 28
  • 121
  • 184
  • 2
    Hmmm. Interesting. Doing some reading on `activeElement`. Wouldn't I just be able to do `elementWithFocus=document.activeElement;` ? – Jonathan M Aug 17 '11 at 16:32
  • 1
    @JonathanM: lol, yup, I just grabbed jQuery's `:focus` filter – qwertymk Aug 17 '11 at 16:34
  • Yes, I think so. This seems to show it: http://help.dottoro.com/external/examples/ljmiswgp/activeElement_1.htm – Jonathan M Aug 17 '11 at 16:34
  • Mod your answer to reflect, and I'll check it. Thanks, qwertymk. – Jonathan M Aug 17 '11 at 16:35
  • 1
    Excellent. Thanks much. Good teamwork between you and Rikudo. :) Give him some upvotes. :D – Jonathan M Aug 17 '11 at 16:37
  • Do ALL current browsers have - at the moment of the blur event - the activeElement? – Wolfgang Adamec Oct 18 '12 at 07:34
  • 2
    @WolfgangAdamec -- apparently not. In some browsers, I'm getting the `body` returned if I don't use setTimeout. In IE8, no timeout needed. In FF 18.0.1, setTimeout **is** needed. In Chrome, it looks like an anchor doesn't get focus. [jsfiddle-ige](http://jsfiddle.net/UtDLS/2/) – ruffin Feb 01 '13 at 18:30
3

In jQuery, at the OP's request:

$(':input').blur(function() {
    $focusedElement = $(':input:focus');
    //Do stuff with $focusedElement
}
Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308
2

Interesting question. The heart of the matter is - when does the 'focus' event fire, before or after the blur event? If it fires before the blur event, the problem is easy, because you can just store the current focus in a variable that your blur event can access.

However, at least in Chrome 13, it appears the blur event happens before the focus event. One possible solution.

Given the following HTML:

<input id="foo" value='foo' />
<input id="bar" value='bar' />

You can then:

var currentFocus;
var pendingBlur;

var foo = document.getElementById('foo');
foo.addEventListener('focus', function(){ 
    currentFocus = foo;
    if(pendingBlur !== undefined){
        pendingBlur();
        pendingBlur = undefined;
    }
});
foo.addEventListener('blur', function(){
    pendingBlur = function(){
        console.log('new focus:', currentFocus);
    };
});

var bar= document.getElementById('bar');
bar.addEventListener('focus', function(){ 
   currentFocus = bar;
   if(pendingBlur !== undefined){
        pendingBlur();
        pendingBlur = undefined;
   }
});
bar.addEventListener('blur', function(){
    pendingBlur = function(){
        console.log('new focus:', currentFocus);
    };
});

Basically, I just not the blur callback so it is handy for the focus event to call after we know about which element was focused.

Here is a working example on JSFiddle.

EDIT: This solution suffers from the problem that if you blur on the form by clicking on something other than another form element, the blur event never fires (since we wait for the focus event). The only way around that, that I can conceive, is using a timer to check if pendingBlur is defined, and if so, call it. At which point you don't really need the focus event to call the blur callback anymore...

Matt
  • 41,216
  • 30
  • 109
  • 147
  • Thanks, Matt. I'll have to digest this after lunch. :) – Jonathan M Aug 17 '11 at 16:38
  • +1 for considering the timing of `blur()` vs. `focus()` and testing it on chrome. However, a perhaps better approach to dealing with the lag between `blur()` and `focus()` may be to employ a `setTimeout()` with the function called by `onblur`. – Jonathan M Aug 17 '11 at 18:50
  • @Jonathan M, yes, I'd be inclined to use a timeout. I just avoided it given your request in the question :) – Matt Aug 17 '11 at 19:10
  • Sorry to throw you off of that. I wasn't necessarily against using setTimeout(), just the solution that involved it. That solution requires setting a variable for every `onclick`...very messy. Thanks much for your help. – Jonathan M Aug 17 '11 at 19:16
0

Year 2020: All major browsers (desktop and mobile) support FocusEvent.relatedTarget.

Martin
  • 5,714
  • 2
  • 21
  • 41
0

event.relatedTarget couldn't find the newly-focused element(was div type in my case), but only null gets returned.

After attaching the attribute tabindex="0" on the element, now it works.

<div id="myDiv">myDiv</div>
console.log(e.relatedTarget) // null

<div id="myDiv" tabindex="0"> tabindexed div </div>
console.log(e.relatedTarget) // <div id="myDiv" tabindex="0">

tabindex is an attribute that make elements focusable using tab key on keyboard. I guess it is mainly for web accessibility.

And I guess you won't need to set tabindex attribute as long as to-be focused elements are already accessible by tab key (such as a, select.. and so on)

JunKim
  • 647
  • 7
  • 17
  • In the latest Chrome, even after adding a tabindex attribute it does not work. However, for me it was the HTML 'button' element on which I was setting the tabindex and wanted the relatedTarget to contain the reference to. However, it's still null. – Piyush Soni Aug 29 '23 at 11:24