235

Suppose I attach an blur function to an HTML input box like this:

<input id="myInput" onblur="function() { ... }"></input>

Is there a way to get the ID of the element which caused the blur event to fire (the element which was clicked) inside the function? How?

For example, suppose I have a span like this:

<span id="mySpan">Hello World</span>

If I click the span right after the input element has focus, the input element will lose its focus. How does the function know that it was mySpan that was clicked?

PS: If the onclick event of the span would occur before the onblur event of the input element my problem would be solved, because I could set some status value indicating a specific element had been clicked.

PPS: The background of this problem is that I want to trigger an AJAX autocompleter control externally (from a clickable element) to show its suggestions, without the suggestions disappearing immediately because of the blur event on the input element. So I want to check in the blur function if one specific element has been clicked, and if so, ignore the blur event.

mikemaccana
  • 110,530
  • 99
  • 389
  • 494
Michiel Borkent
  • 34,228
  • 15
  • 86
  • 149
  • 1
    This is an interesting question that I'd love to see reasoning behind - i.e. why are you doing this? What is the context? – Rahul Sep 23 '08 at 15:39
  • Rahul and roosteronacid, I updated the question as a reaction to your comments (the PPS). – Michiel Borkent Sep 23 '08 at 17:36
  • 1
    As this info is a bit old, see here for a newer answer: http://stackoverflow.com/questions/7096120/how-to-determine-where-focus-went – Jonathan M Aug 17 '11 at 19:03

23 Answers23

114

2015 answer: according to UI Events, you can use the relatedTarget property of the event:

Used to identify a secondary EventTarget related to a Focus event, depending on the type of event.

For blur events,

relatedTarget: event target receiving focus.

Example:

function blurListener(event) {
  event.target.className = 'blurred';
  if(event.relatedTarget)
    event.relatedTarget.className = 'focused';
}
[].forEach.call(document.querySelectorAll('input'), function(el) {
  el.addEventListener('blur', blurListener, false);
});
.blurred { background: orange }
.focused { background: lime }
<p>Blurred elements will become orange.</p>
<p>Focused elements should become lime.</p>
<input /><input /><input />

Note Firefox won't support relatedTarget until version 48 (bug 962251, MDN).

Oriol
  • 274,082
  • 63
  • 437
  • 513
  • Firefox doesn't have support, but there is a ticket: https://bugzilla.mozilla.org/show_bug.cgi?id=687787 – sandstrom Apr 18 '16 at 16:04
  • 5
    Now it has. See [here](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget). – rplaurindo Dec 02 '16 at 05:05
  • 2
    love the web community, now everything is a lot easier ^_^ – am05mhz May 20 '17 at 02:53
  • 13
    Great solution. Unfortunately, `relatedTarget` will return `null` if the target receiving focus is not an input element. – Pedro Hoehl Carvalho Sep 15 '17 at 19:56
  • 16
    If receiving focus element is not an input element, add a `tabIndex="0"` attribute to it and it will work – Dany Dec 08 '17 at 12:27
  • Also note that no macOS/iOS `relatedTarget` is `null` [when a ` – Nickolay Aug 30 '19 at 15:44
95

Hmm... In Firefox, you can use explicitOriginalTarget to pull the element that was clicked on. I expected toElement to do the same for IE, but it does not appear to work... However, you can pull the newly-focused element from the document:

function showBlur(ev)
{
   var target = ev.explicitOriginalTarget||document.activeElement;
   document.getElementById("focused").value = 
      target ? target.id||target.tagName||target : '';
}

...

<button id="btn1" onblur="showBlur(event)">Button 1</button>
<button id="btn2" onblur="showBlur(event)">Button 2</button>
<button id="btn3" onblur="showBlur(event)">Button 3</button>
<input id="focused" type="text" disabled="disabled" />

Caveat: This technique does not work for focus changes caused by tabbing through fields with the keyboard, and does not work at all in Chrome or Safari. The big problem with using activeElement (except in IE) is that it is not consistently updated until after the blur event has been processed, and may have no valid value at all during processing! This can be mitigated with a variation on the technique Michiel ended up using:

function showBlur(ev)
{
  // Use timeout to delay examination of activeElement until after blur/focus 
  // events have been processed.
  setTimeout(function()
  {
    var target = document.activeElement;
    document.getElementById("focused").value = 
      target ? target.id||target.tagName||target : '';
  }, 1);
}

This should work in most modern browsers (tested in Chrome, IE, and Firefox), with the caveat that Chrome does not set focus on buttons that are clicked (vs. tabbed to).

Community
  • 1
  • 1
Shog9
  • 156,901
  • 35
  • 231
  • 235
  • 1
    I have been looking for something like explicitOriginalTarget for a long time. How did you discover it? – Kev Sep 23 '08 at 16:05
  • 3
    Examined the event object in FireBug. FWIW, the property is Mozilla-specific, and documented on MDC: http://developer.mozilla.org/en/DOM/event.explicitOriginalTarget – Shog9 Sep 23 '08 at 16:06
  • You saved my life! Today I've been fighting with this for hours until I found this tip :) Thanks, Joshua! – Danita Mar 18 '09 at 22:23
  • 2
    This doesn't work (or stopped working?) in Firefox 3.6 and Safari 4.0.3 in Windows. – Chetan S Jan 30 '10 at 00:13
  • @Chetan Sastry: It still works in FF 3.6, but... Only in limited circumstances. I've updated this answer with more information. – Shog9 Jan 30 '10 at 01:31
  • As this information is a bit old, see here for a newer answer: http://stackoverflow.com/questions/7096120/how-to-determine-where-focus-went – Jonathan M Aug 17 '11 at 19:02
  • @Jonathan: um... The accepted answer there uses activeElement, which works in IE but fails (for this purpose) everywhere else. The only reliable solution I'm aware of uses setTimeout to delay retrieval until after the event has been processed... (I guess I should probably note that though) – Shog9 Aug 17 '11 at 20:44
  • @Shog9: activeElement documentation shows almost all browsers support it. I'm confused by "fails (for this purpose) everywhere else". Can you explain? – Jonathan M Aug 17 '11 at 20:51
  • 1
    @Jonathan: the property is available, but is not updated when the `blur` event fires. I've updated the answer with details. Have you tried using activeElement for this in Chrome or Firefox? It gives you the element being blurred... – Shog9 Aug 17 '11 at 21:07
  • @Shog. I see. There was an answer to my question that involved setTimeout(). That combined with activeElement was mentioned over on the other question. Good update. Thanks. – Jonathan M Aug 17 '11 at 21:14
  • Thank you very much, helped me to fix a yar old problem. – simon Jul 10 '14 at 13:08
  • 1
    Actually not working on FF 31: explicitOriginalTarget show the current blur target, document.activeElement is null on blur event. – Adrian Maire Aug 05 '14 at 16:51
  • it does not help much in practice, blur does not always happens because we clicked focussable element. and if it's the case this it returns the whole body which is for few help for most. I saw the solution of @EricDuWeb down here and don't know why it's not the best? – fekiri malek Sep 02 '16 at 06:14
  • Eric's answer handles a keyup event and returns the element that generated the event, @fekiri - neither one of which is what this question is asking for. – Shog9 Sep 02 '16 at 22:04
21

I solved it eventually with a timeout on the onblur event (thanks to the advice of a friend who is not StackOverflow):

<input id="myInput" onblur="setTimeout(function() {alert(clickSrc);},200);"></input>
<span onclick="clickSrc='mySpan';" id="mySpan">Hello World</span>

Works both in FF and IE.

Michiel Borkent
  • 34,228
  • 15
  • 86
  • 149
  • 11
    Is this considered bad practice in javascript world btw? – Michiel Borkent Sep 24 '08 at 17:18
  • 1
    old post but I have the same question. regardless, it's the only thing that seems to get the job done cross-browser. – joshs Jun 24 '10 at 22:10
  • this answer helped my a TON by solving some other problem... Thanks Michael! – Alex from Jitbit Dec 29 '10 at 19:06
  • 1
    @MichielBorkent this is a bad implementation because it's not event driven. You wait a given amount of time and just hope that it's been long enough. The longer you wait the safer you are, but also the more time you'll waste if you didn't need to wait so long. Maybe someone is on a really old/slow device and this timeout isn't enough. Unfortunately I'm here because the events aren't providing me what I need to determine if an iframe is being clicked on to in FireFox, yet it works for all other browsers. I'm so tempted to use this method now even though it is bad practice. – CTS_AE Sep 05 '18 at 00:23
  • 1
    This question is pretty old, it goes back to 2008. Meanwhile there may have been better approaches. Can you try the 2015 answer? – Michiel Borkent Sep 05 '18 at 07:27
  • Note that `blur` happens on `mousedown`, and `click` is registered after `mouseup`, any amount of time can pass between those. When the user is not tapping, a relatively slow click can easily take hundreds of ms. – Nickolay Aug 30 '19 at 15:53
17

It's possible to use mousedown event of document instead of blur:

$(document).mousedown(function(){
  if ($(event.target).attr("id") == "mySpan") {
    // some process
  }
});
Evgeny Shmanev
  • 771
  • 6
  • 11
10

The instance of type FocusEvent has the relatedTarget property, however, up to version 47 of the FF, specifically, this attribute returns null, from 48 it already works.

You can see more here.

rplaurindo
  • 1,277
  • 14
  • 23
3

Works in Google Chrome v66.x, Mozilla v59.x and Microsoft Edge... Solution with jQuery.

I test in Internet Explorer 9 and not supported.

$("#YourElement").blur(function(e){
     var InputTarget =  $(e.relatedTarget).attr("id"); // GET ID Element
     console.log(InputTarget);
     if(target == "YourId") { // If you want validate or make a action to specfic element
          ... // your code
     }
});

Comment your test in others internet explorer versions.

LuisEduardox
  • 364
  • 4
  • 9
2

Can you reverse what you're checking and when? That is if you remeber what was blurred last:

<input id="myInput" onblur="lastBlurred=this;"></input>

and then in the onClick for your span, call function() with both objects:

<span id="mySpan" onClick="function(lastBlurred, this);">Hello World</span>

Your function could then decide whether or not to trigger the Ajax.AutoCompleter control. The function has the clicked object and the blurred object. The onBlur has already happened so it won't make the suggestions disappear.

bmb
  • 6,058
  • 2
  • 37
  • 58
2

I am also trying to make Autocompleter ignore blurring if a specific element clicked and have a working solution, but for only Firefox due to explicitOriginalTarget

Autocompleter.Base.prototype.onBlur = Autocompleter.Base.prototype.onBlur.wrap( 
        function(origfunc, ev) {
            if ($(this.options.ignoreBlurEventElement)) {
                var newTargetElement = (ev.explicitOriginalTarget.nodeType == 3 ? ev.explicitOriginalTarget.parentNode : ev.explicitOriginalTarget);
                if (!newTargetElement.descendantOf($(this.options.ignoreBlurEventElement))) {
                    return origfunc(ev);
                }
            }
        }
    );

This code wraps default onBlur method of Autocompleter and checks if ignoreBlurEventElement parameters is set. if it is set, it checks everytime to see if clicked element is ignoreBlurEventElement or not. If it is, Autocompleter does not cal onBlur, else it calls onBlur. The only problem with this is that it only works in Firefox because explicitOriginalTarget property is Mozilla specific . Now I am trying to find a different way than using explicitOriginalTarget. The solution you have mentioned requires you to add onclick behaviour manually to the element. If I can't manage to solve explicitOriginalTarget issue, I guess I will follow your solution.

matte
  • 1,196
  • 4
  • 13
  • 21
1

i think it's not possibe, with IE you can try to use window.event.toElement, but it dosn't work with firefox!

T J
  • 42,762
  • 13
  • 83
  • 138
stefano m
  • 4,094
  • 5
  • 28
  • 27
  • Doesn't work with Chrome or Safari. Maybe won't work with new versions of IE that are more standards compatible. – robocat May 26 '15 at 04:02
1

You can fix IE with :

 event.currentTarget.firstChild.ownerDocument.activeElement

It looks like "explicitOriginalTarget" for FF.

Antoine And J

Madbean
  • 11
  • 1
1

As noted in this answer, you can check the value of document.activeElement. document is a global variable, so you don't have to do any magic to use it in your onBlur handler:

function myOnBlur(e) {
  if(document.activeElement ===
       document.getElementById('elementToCheckForFocus')) {
    // Focus went where we expected!
    // ...
  }
}
Community
  • 1
  • 1
Kevin
  • 14,655
  • 24
  • 74
  • 124
1
  • document.activeElement could be a parent node (for example body node because it is in a temporary phase switching from a target to another), so it is not usable for your scope
  • ev.explicitOriginalTarget is not always valued

So the best way is to use onclick on body event for understanding indirectly your node(event.target) is on blur

1

Use something like this:

var myVar = null;

And then inside your function:

myVar = fldID;

And then:

setTimeout(setFocus,1000)

And then:

function setFocus(){ document.getElementById(fldID).focus(); }

Final code:

<html>
<head>
    <script type="text/javascript">
        function somefunction(){
            var myVar = null;

            myVar = document.getElementById('myInput');

            if(myVar.value=='')
                setTimeout(setFocusOnJobTitle,1000);
            else
                myVar.value='Success';
        }
        function setFocusOnJobTitle(){
            document.getElementById('myInput').focus();
        }
    </script>
</head>
<body>
<label id="jobTitleId" for="myInput">Job Title</label>
<input id="myInput" onblur="somefunction();"></input>
</body>
</html>
Benoit Garret
  • 14,027
  • 4
  • 59
  • 64
Vikas
  • 11
  • 1
0

Edit: A hacky way to do it would be to create a variable that keeps track of focus for every element you care about. So, if you care that 'myInput' lost focus, set a variable to it on focus.

<script type="text/javascript">
   var lastFocusedElement;
</script>
<input id="myInput" onFocus="lastFocusedElement=this;" />

Original Answer: You can pass 'this' to the function.

<input id="myInput" onblur="function(this){
   var theId = this.id; // will be 'myInput'
}" />
brock.holum
  • 3,133
  • 2
  • 20
  • 15
0

I suggest using global variables blurfrom and blurto. Then, configure all elements you care about to assign their position in the DOM to the variable blurfrom when they lose focus. Additionally, configure them so that gaining focus sets the variable blurto to their position in the DOM. Then, you could use another function altogether to analyze the blurfrom and blurto data.

stalepretzel
  • 15,543
  • 22
  • 76
  • 91
  • It's almost the solution, but in the onblur event handler I already needed to know what item had been clicked, so a timeout on the onblur did it for me. – Michiel Borkent Sep 24 '08 at 17:27
0

I wrote an alternative solution how to make any element focusable and "blurable".

It's based on making an element as contentEditable and hiding visually it and disabling edit mode itself:

el.addEventListener("keydown", function(e) {
  e.preventDefault();
  e.stopPropagation();
});

el.addEventListener("blur", cbBlur);
el.contentEditable = true;

DEMO

Note: Tested in Chrome, Firefox, and Safari (OS X). Not sure about IE.


Related: I was searching for a solution for VueJs, so for those who interested/curious how to implement such functionality using Vue Focusable directive, please take a look.

Serhii Matrunchyk
  • 9,083
  • 6
  • 34
  • 47
0

I've been playing with this same feature and found out that FF, IE, Chrome and Opera have the ability to provide the source element of an event. I haven't tested Safari but my guess is it might have something similar.

$('#Form').keyup(function (e) {
    var ctrl = null;
    if (e.originalEvent.explicitOriginalTarget) { // FF
        ctrl = e.originalEvent.explicitOriginalTarget;
    }
    else if (e.originalEvent.srcElement) { // IE, Chrome and Opera
        ctrl = e.originalEvent.srcElement;
    }
    //...
});
EricDuWeb
  • 11
  • 1
0

keep in mind, that the solution with explicitOriginalTarget does not work for text-input-to-text-input jumps.

try to replace buttons with the following text-inputs and you will see the difference:

<input id="btn1" onblur="showBlur(event)" value="text1">
<input id="btn2" onblur="showBlur(event)" value="text2">
<input id="btn3" onblur="showBlur(event)" value="text3">
0

I do not like using timeout when coding javascript so I would do it the opposite way of Michiel Borkent. (Did not try the code behind but you should get the idea).

<input id="myInput" onblur="blured = this.id;"></input>
<span onfocus = "sortOfCallback(this.id)" id="mySpan">Hello World</span>

In the head something like that

<head>
    <script type="text/javascript">
        function sortOfCallback(id){
            bluredElement = document.getElementById(blured);
            // Do whatever you want on the blured element with the id of the focus element


        }

    </script>
</head>
Sunil D.
  • 17,983
  • 6
  • 53
  • 65
Ronan Quillevere
  • 3,699
  • 1
  • 29
  • 44
-2

I see only hacks in the answers, but there's actually a builtin solution very easy to use : Basically you can capture the focus element like this:

const focusedElement = document.activeElement

https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/activeElement

xpy
  • 5,481
  • 3
  • 29
  • 48
Thomas J.
  • 147
  • 3
  • 2
    At least in my case, this does not work as the activeElement is the body, if I check this on the next render/tick it is set correctly. But it seems that shog9's solution is the only proper solution as it makes sure a tick has been tocking. – Mathijs Segers Feb 20 '19 at 14:27
  • This was my first thought, but it doesn't work. – Brendan Nov 29 '22 at 15:20
-3


I think its easily possible via jquery by passing the reference of the field causing the onblur event in "this".
For e.g.

<input type="text" id="text1" onblur="showMessageOnOnblur(this)">

function showMessageOnOnblur(field){
    alert($(field).attr("id"));
}

Thanks
Monika

-3

This way:

<script type="text/javascript">
    function yourFunction(element) {
        alert(element);
    }
</script>
<input id="myinput" onblur="yourFunction(this)">

Or if you attach the listener via JavaScript (jQuery in this example):

var input = $('#myinput').blur(function() {
    alert(this);
});

Edit: sorry. I misread the question.

Armin Ronacher
  • 31,998
  • 13
  • 65
  • 69
  • 3
    If you're aware that this is not the answer for the question and you misread it, then please update it accordingly or delete it. – T J Sep 04 '14 at 19:09
-3

You could make it like this:

<script type="text/javascript">
function myFunction(thisElement) 
{
    document.getElementByName(thisElement)[0];
}
</script>
<input type="text" name="txtInput1" onBlur="myFunction(this.name)"/>