ChatGPT解决这个技术问题 Extra ChatGPT

When a 'blur' event occurs, how can I find out which element focus went *to*?

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.

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 and roosteronacid, I updated the question as a reaction to your comments (the PPS).
As this info is a bit old, see here for a newer answer: stackoverflow.com/questions/7096120/…

O
Oriol

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 }

Blurred elements will become orange.

Focused elements should become lime.

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


Firefox doesn't have support, but there is a ticket: bugzilla.mozilla.org/show_bug.cgi?id=687787
Now it has. See here.
love the web community, now everything is a lot easier ^_^
Great solution. Unfortunately, relatedTarget will return null if the target receiving focus is not an input element.
If receiving focus element is not an input element, add a tabIndex="0" attribute to it and it will work
C
Community

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).


I have been looking for something like explicitOriginalTarget for a long time. How did you discover it?
Examined the event object in FireBug. FWIW, the property is Mozilla-specific, and documented on MDC: developer.mozilla.org/en/DOM/event.explicitOriginalTarget
This doesn't work (or stopped working?) in Firefox 3.6 and Safari 4.0.3 in Windows.
@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...
Actually not working on FF 31: explicitOriginalTarget show the current blur target, document.activeElement is null on blur event.
M
Michiel Borkent

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.


Is this considered bad practice in javascript world btw?
old post but I have the same question. regardless, it's the only thing that seems to get the job done cross-browser.
this answer helped my a TON by solving some other problem... Thanks Michael!
@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.
This question is pretty old, it goes back to 2008. Meanwhile there may have been better approaches. Can you try the 2015 answer?
E
Evgeny Shmanev

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

$(document).mousedown(function(){
  if ($(event.target).attr("id") == "mySpan") {
    // some process
  }
});

r
rplaurindo

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.


L
LuisEduardox

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.


m
matte

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.


b
bmb

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.


B
Benoit Garret

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>

T
T J

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


Doesn't work with Chrome or Safari. Maybe won't work with new versions of IE that are more standards compatible.
M
Madbean

You can fix IE with :

 event.currentTarget.firstChild.ownerDocument.activeElement

It looks like "explicitOriginalTarget" for FF.

Antoine And J


C
Community

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!
    // ...
  }
}

佚名

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


b
brock.holum

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'
}" />

this doesn't answer the question, hopefully I made it clearer with the additional example
s
stalepretzel

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.


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.
佚名

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">

E
EricDuWeb

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;
    }
    //...
});

this works fine why downvoting? just remove originalEvent!
S
Sunil D.

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>

S
Serhii Matrunchyk

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.


x
xpy

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


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.
A
Armin Ronacher

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.


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.
M
Monika Sharma

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


S
Shikekaka Yamiryuukido

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)"/>