Lorsqu'un événement «flou» se produit, comment puis-je savoir sur quel élément le focus est allé * vers *?

188

Supposons que j'attache une blurfonction à une zone de saisie HTML comme celle-ci:

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

Existe-t-il un moyen d'obtenir l'ID de l'élément qui a provoqué le déclenchement de l' blurévénement (l'élément sur lequel on a cliqué) à l'intérieur de la fonction? Comment?

Par exemple, supposons que j'ai une envergure comme celle-ci:

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

Si je clique sur la plage juste après que l'élément d'entrée a le focus, l'élément d'entrée perdra son focus. Comment la fonction sait-elle que c'est ce mySpanqui a été cliqué?

PS: Si l'événement onclick de la plage se produisait avant l'événement onblur de l'élément d'entrée, mon problème serait résolu, car je pourrais définir une valeur de statut indiquant qu'un élément spécifique a été cliqué.

PPS: Le fond de ce problème est que je veux déclencher un contrôle de saisie semi-automatique AJAX en externe (à partir d'un élément cliquable) pour afficher ses suggestions, sans que les suggestions ne disparaissent immédiatement à cause de l' blurévénement sur l'élément d'entrée. Je veux donc vérifier dans la blurfonction si un élément spécifique a été cliqué, et si c'est le cas, ignorer l'événement de flou.

Michiel Borkent
la source
C'est une question intéressante sur laquelle j'aimerais voir le raisonnement derrière - c'est-à-dire pourquoi faites-vous cela? Quel est le contexte?
Rahul
Rahul et roosteronacid, j'ai mis à jour la question en réaction à vos commentaires (le PPS).
Michiel Borkent
1
Comme cette information est un peu ancienne, voir ici pour une réponse plus récente: stackoverflow.com/questions/7096120/…
Jonathan M

Réponses:

86

Hmm ... Dans Firefox, vous pouvez utiliser explicitOriginalTargetpour tirer l'élément sur lequel vous avez cliqué. Je m'attendais toElementà faire de même pour IE, mais cela ne semble pas fonctionner ... Cependant, vous pouvez extraire l'élément nouvellement ciblé du 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" />

Attention: cette technique ne fonctionne pas pour les changements de focus provoqués par la tabulation dans les champs avec le clavier, et ne fonctionne pas du tout dans Chrome ou Safari. Le gros problème avec l'utilisation activeElement(sauf dans IE) est qu'il n'est mis à jour de manière cohérente qu'après le blurtraitement de l' événement, et peut n'avoir aucune valeur valide pendant le traitement! Cela peut être atténué par une variation de la technique que Michiel a finalement utilisée :

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

Cela devrait fonctionner dans la plupart des navigateurs modernes (testés dans Chrome, IE et Firefox), avec l'avertissement que Chrome ne met pas l'accent sur les boutons sur lesquels on clique (par rapport aux onglets).

Shog9
la source
1
Je cherchais quelque chose comme explicitOriginalTarget depuis longtemps. Comment l'avez-vous découvert?
Kev
3
Examen de l'objet événement dans FireBug. FWIW, la propriété est spécifique à Mozilla et documentée sur MDC: developer.mozilla.org/en/DOM/event.explicitOriginalTarget
Shog9
2
Cela ne fonctionne pas (ou a cessé de fonctionner?) Dans Firefox 3.6 et Safari 4.0.3 sous Windows.
Chetan S
1
@Jonathan: la propriété est disponible, mais n'est pas mise à jour lorsque l' blurévénement se déclenche. J'ai mis à jour la réponse avec des détails. Avez-vous essayé d'utiliser activeElement pour cela dans Chrome ou Firefox? Cela vous donne l'élément flou ...
Shog9
1
En fait, ne fonctionne pas sur FF 31: explicitOriginalTarget affiche la cible de flou actuelle, document.activeElement est nul sur l'événement de flou.
Adrian Maire
76

Réponse 2015 : selon UI Events , vous pouvez utiliser la relatedTargetpropriété de l'événement:

Utilisé pour identifier un secondaire EventTargetlié à un événement Focus, en fonction du type d'événement.

Pour les blurévénements,

relatedTarget: cible de l'événement recevant le focus.

Exemple:

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

Remarque Firefox ne supportera pas relatedTargetavant la version 48 ( bogue 962251 , MDN ).

Oriol
la source
Firefox n'a pas de support, mais il y a un ticket: bugzilla.mozilla.org/show_bug.cgi?id=687787
sandstrom
4
Maintenant c'est le cas. Regardez ici .
rplaurindo
1
J'adore la communauté web, maintenant tout est beaucoup plus facile ^ _ ^
am05mhz
7
Excellente solution. Malheureusement, relatedTargetretournera nullsi la cible recevant le focus n'est pas un élément d'entrée.
Pedro Hoehl Carvalho
6
Si la réception de l'élément de focus n'est pas un élément d'entrée, ajoutez-y un tabIndex="0"attribut et cela fonctionnera
Dany
19

Je l'ai finalement résolu avec un timeout sur l'événement onblur (grâce aux conseils d'un ami qui n'est pas StackOverflow):

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

Fonctionne à la fois dans FF et IE.

Michiel Borkent
la source
9
Est-ce considéré comme une mauvaise pratique dans le monde javascript btw?
Michiel Borkent
1
ancien poste mais j'ai la même question. peu importe, c'est la seule chose qui semble faire le travail entre les navigateurs.
joshs
cette réponse m'a aidé à résoudre un autre problème ... Merci Michael!
Alex
@MichielBorkent c'est une mauvaise implémentation car elle n'est pas pilotée par les événements. Vous attendez un laps de temps donné et espérez simplement que cela fait assez longtemps. Plus vous attendez, plus vous êtes en sécurité, mais aussi plus vous perdrez de temps si vous n'aviez pas besoin d'attendre si longtemps. Peut-être que quelqu'un utilise un appareil très ancien / lent et que ce délai d'attente n'est pas suffisant. Malheureusement, je suis ici parce que les événements ne me fournissent pas ce dont j'ai besoin pour déterminer si une iframe est cliquée dans FireFox, mais cela fonctionne pour tous les autres navigateurs. Je suis tellement tenté d'utiliser cette méthode maintenant même si c'est une mauvaise pratique.
CTS_AE
1
Cette question est assez ancienne, elle remonte à 2008. En attendant, il y a peut-être eu de meilleures approches. Pouvez-vous essayer la réponse de 2015?
Michiel Borkent
16

Il est possible d'utiliser l'événement mousedown du document au lieu du flou:

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

Les FocusEventinstances de type ont un relatedTargetattribut, cependant, jusqu'à la version 47 du FF, spécifiquement, cet attribut renvoie null, à partir de 48 fonctionne déjà.

Vous pouvez en voir plus ici .

rplaurindo
la source
2

J'essaie également de faire en sorte que la saisie semi-automatique ignore le flou si un élément spécifique a cliqué et a une solution fonctionnelle, mais uniquement pour Firefox en raison de 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);
                }
            }
        }
    );

Ce code encapsule la méthode onBlur par défaut de la saisie semi-automatique et vérifie si les paramètres ignoreBlurEventElement sont définis. s'il est défini, il vérifie à chaque fois si l'élément cliqué est ignoreBlurEventElement ou non. Si tel est le cas, la saisie semi-automatique n'appelle pas onBlur, sinon il appelle onBlur. Le seul problème avec ceci est que cela ne fonctionne que dans Firefox car la propriété explicitOriginalTarget est spécifique à Mozilla. Maintenant, j'essaie de trouver une manière différente d'utiliser explicitOriginalTarget. La solution que vous avez mentionnée nécessite que vous ajoutiez manuellement le comportement onclick à l'élément. Si je ne parviens pas à résoudre le problème explicitOriginalTarget, je suppose que je suivrai votre solution.

mat
la source
2

Pouvez-vous inverser ce que vous vérifiez et quand? C'est si vous vous souvenez de ce qui était flou en dernier:

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

puis dans le onClick pour votre span, appelez function () avec les deux objets:

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

Votre fonction pourrait alors décider de déclencher ou non le contrôle Ajax.AutoCompleter. La fonction a l'objet cliqué et l'objet flou. Le onBlur est déjà arrivé, donc il ne fera pas disparaître les suggestions.

bmb
la source
1

Utilisez quelque chose comme ceci:

var myVar = null;

Et puis à l'intérieur de votre fonction:

myVar = fldID;

Puis:

setTimeout(setFocus,1000)

Puis:

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

Code final:

<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>
Vikas
la source
1

Je pense que ce n'est pas possible, avec IE, vous pouvez essayer d'utiliser window.event.toElement, mais cela ne fonctionne pas avec Firefox!

stefano m
la source
Ne fonctionne pas avec Chrome ou Safari. Peut-être ne fonctionnera pas avec les nouvelles versions d'IE qui sont plus compatibles avec les normes.
robocat
1

Comme indiqué dans cette réponse , vous pouvez vérifier la valeur de document.activeElement. documentest une variable globale, vous n'avez donc pas besoin de faire de magie pour l'utiliser dans votre gestionnaire onBlur:

function myOnBlur(e) {
  if(document.activeElement ===
       document.getElementById('elementToCheckForFocus')) {
    // Focus went where we expected!
    // ...
  }
}
Kevin
la source
1
  • document.activeElement peut être un nœud parent (par exemple, un nœud de corps car il est dans une phase temporaire de passage d'une cible à une autre), il n'est donc pas utilisable pour votre portée
  • ev.explicitOriginalTarget n'est pas toujours valorisé

Le meilleur moyen est donc d'utiliser onclick on body event pour comprendre indirectement que votre nœud (event.target) est sur le flou


la source
1

Fonctionne sous Google Chrome v66.x, Mozilla v59.x et Microsoft Edge ... Solution avec jQuery.

Je teste dans Internet Explorer 9 et n'est pas pris en charge.

$("#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
     }
});

Commentez votre test dans d'autres versions d'Internet Explorer.

LuisEduardox
la source
0

Edit: Une manière piratée de le faire serait de créer une variable qui garde la trace de la mise au point pour chaque élément qui vous tient à cœur. Donc, si vous vous souciez que «myInput» ait perdu le focus, définissez-lui une variable sur focus.

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

Réponse originale: vous pouvez transmettre «ceci» à la fonction.

<input id="myInput" onblur="function(this){
   var theId = this.id; // will be 'myInput'
}" />
brock.holum
la source
cela ne répond pas à la question, j'espère que je l'ai rendu plus clair avec l'exemple supplémentaire
Michiel Borkent
0

Je suggère d'utiliser des variables globales blurfrom et blurto. Ensuite, configurez tous les éléments qui vous intéressent pour affecter leur position dans le DOM à la variable blurfrom lorsqu'ils perdent le focus. De plus, configurez-les de manière à ce que la mise en évidence définisse la variable blurto à sa position dans le DOM. Ensuite, vous pouvez utiliser une autre fonction pour analyser les données floufrom et blurto.

stalepretzel
la source
C'est presque la solution, mais dans le gestionnaire d'événements onblur, j'avais déjà besoin de savoir sur quel élément avait été cliqué, donc un délai d'attente sur onblur l'a fait pour moi.
Michiel Borkent
0

gardez à l'esprit que la solution avec explicitOriginalTarget ne fonctionne pas pour les sauts de saisie de texte à saisie de texte.

essayez de remplacer les boutons par les entrées de texte suivantes et vous verrez la différence:

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

la source
0

J'ai joué avec cette même fonctionnalité et j'ai découvert que FF, IE, Chrome et Opera ont la capacité de fournir l'élément source d'un événement. Je n'ai pas testé Safari, mais je suppose qu'il pourrait avoir quelque chose de similaire.

$('#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
la source
cela fonctionne bien pourquoi voter contre? supprimez simplement originalEvent!
fekiri malek
0

Je n'aime pas utiliser timeout lors du codage de javascript, donc je le ferais à l'opposé de Michiel Borkent. (Je n'ai pas essayé le code derrière mais vous devriez avoir l'idée).

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

Dans la tête quelque chose comme ça

<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>
Ronan Quillevere
la source
0

Vous pouvez réparer IE avec:

 event.currentTarget.firstChild.ownerDocument.activeElement

Cela ressemble à "explicitOriginalTarget" pour FF.

Antoine et J

Madbean
la source
0

J'ai écrit une solution alternative pour rendre n'importe quel élément focalisable et "flou".

Il est basé sur la création d'un élément en tant que contentEditable, son masquage visuel et la désactivation du mode d'édition lui-même:

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

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

DEMO

Remarque: testé dans Chrome, Firefox et Safari (OS X). Pas sûr d'IE.


En relation: Je cherchais une solution pour VueJs, donc pour ceux qui sont intéressés / curieux de savoir comment implémenter une telle fonctionnalité à l'aide de la directive Vue Focusable, veuillez jeter un coup d'œil .

Serhii Matrunchyk
la source
-2

Par ici:

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

Ou si vous attachez l'écouteur via JavaScript (jQuery dans cet exemple):

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

Edit : désolé. J'ai mal lu la question.

Armin Ronacher
la source
3
Si vous savez que ce n'est pas la réponse à la question et que vous l'avez mal interprétée, veuillez la mettre à jour en conséquence ou la supprimer.
TJ
-2


Je pense que c'est facilement possible via jquery en passant la référence du champ provoquant l'événement onblur dans "this".
Pour par exemple

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

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

Merci
Monika

Monika Sharma
la source
-2

Vous pourriez faire comme ceci:

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

Je ne vois que des hacks dans les réponses, mais il existe en fait une solution intégrée très facile à utiliser: En gros, vous pouvez capturer l'élément de focus comme ceci:

const focusedElement = document.activeElement

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

Thomas J.
la source
Au moins dans mon cas, cela ne fonctionne pas car le activeElement est le corps, si je le vérifie lors du prochain rendu / coche, il est correctement défini. Mais il semble que la solution de shog9 soit la seule solution appropriée car elle s'assure qu'une tique a bien tourné.
Mathijs Segers