Cuando ocurre un evento de 'desenfoque', ¿cómo puedo saber qué elemento se enfocó * a *?

188

Supongamos que adjunto un blur función a un cuadro de entrada HTML como este:

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

¿Hay alguna forma de obtener la ID del elemento que provocó el blurevento (el elemento en el que se hizo clic) dentro de la función? ¿Cómo?

Por ejemplo, supongamos que tengo un lapso como este:

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

Si hago clic en el intervalo justo después de que el elemento de entrada tenga el foco, el elemento de entrada perderá su foco. ¿Cómo sabe la función que se mySpanhizo clic en ella?

PD: Si el evento onclick del intervalo se produjera antes del evento onblur del elemento de entrada, mi problema se resolvería, porque podría establecer algún valor de estado que indicara que se hizo clic en un elemento específico.

PPS: El fondo de este problema es que quiero activar un control de autocompletador AJAX externamente (desde un elemento en el que se puede hacer clic) para mostrar sus sugerencias, sin que las sugerencias desaparezcan inmediatamente debido al blurevento en el elemento de entrada. Así que quiero revisar elblur función si se ha hecho clic en un elemento específico y, de ser así, ignorar el evento de desenfoque.

Michiel Borkent
fuente
Esta es una pregunta interesante que me encantaría ver razonamiento detrás, es decir, ¿por qué haces esto? ¿Cuál es el contexto?
Rahul
Rahul y roosteronacid, actualicé la pregunta como reacción a sus comentarios (el PPS).
Michiel Borkent
1
Como esta información es un poco vieja, vea aquí para obtener una respuesta más nueva: stackoverflow.com/questions/7096120/…
Jonathan M

Respuestas:

86

Hmm ... En Firefox, puede usar explicitOriginalTargetpara extraer el elemento en el que se hizo clic. Esperaba toElementhacer lo mismo para IE, pero no parece funcionar ... Sin embargo, puede extraer el elemento recién enfocado del documento:

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

Advertencia: esta técnica no funciona para los cambios de enfoque causados ​​por la tabulación de los campos con el teclado, y no funciona en absoluto en Chrome o Safari. ¡El gran problema con el uso activeElement(excepto en IE) es que no se actualiza constantemente hasta después deblur que se haya procesado el evento, y puede que no tenga ningún valor válido durante el procesamiento! Esto se puede mitigar con una variación en la técnica que Michiel terminó usando :

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

Esto debería funcionar en la mayoría de los navegadores modernos (probados en Chrome, IE y Firefox), con la advertencia de que Chrome no establece el foco en los botones en los que se hace clic (en comparación con pestañas).

Shog9
fuente
1
He estado buscando algo como explícitoOriginalTarget durante mucho tiempo. ¿Cómo lo descubriste?
Kev
3
Examinó el objeto de evento en FireBug. FWIW, la propiedad es específica de Mozilla y está documentada en MDC: developer.mozilla.org/en/DOM/event.explicitOriginalTarget
Shog9
2
Esto no funciona (¿o dejó de funcionar?) En Firefox 3.6 y Safari 4.0.3 en Windows.
Chetan S
1
@ Jonathan: la propiedad está disponible, pero no se actualiza cuando se activa el blurevento. He actualizado la respuesta con detalles. ¿Has intentado usar activeElement para esto en Chrome o Firefox? Te da el elemento que está siendo borroso ...
#
1
En realidad no funciona en FF 31: explicitOriginalTarget muestra el objetivo de desenfoque actual, document.activeElement es nulo en el evento de desenfoque.
Adrian Maire
75

Respuesta de 2015 : según UI Events , puede usar la relatedTargetpropiedad del evento:

Se usa para identificar un secundario EventTargetrelacionado con un evento de Focus, según el tipo de evento.

Para blureventos,

relatedTarget: objetivo del evento que recibe el foco.

Ejemplo:

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

Nota: Firefox no será compatible relatedTargethasta la versión 48 ( error 962251 , MDN ).

Oriol
fuente
Firefox no tiene soporte, pero no es un boleto: bugzilla.mozilla.org/show_bug.cgi?id=687787
SANDSTRÓM
3
Ahora lo ha hecho. Ver aquí .
rplaurindo
amo a la comunidad web, ahora todo es mucho más fácil ^ _ ^
am05mhz
66
Gran solución Desafortunadamente, relatedTargetregresará nullsi el objetivo que recibe el foco no es un elemento de entrada.
Pedro Hoehl Carvalho
55
Si recibir un elemento de enfoque no es un elemento de entrada, agréguele un tabIndex="0"atributo y funcionará
Dany
19

Finalmente lo resolví con un tiempo de espera en el evento onblur (gracias al consejo de un amigo que no es StackOverflow):

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

Funciona tanto en FF como en IE.

Michiel Borkent
fuente
9
¿Se considera esto una mala práctica en javascript world por cierto?
Michiel Borkent
1
Publicación anterior pero tengo la misma pregunta. de todos modos, es lo único que parece hacer el trabajo en varios navegadores.
joshs
esta respuesta me ayudó mucho al resolver algún otro problema ... ¡Gracias Michael!
Alex
@MichielBorkent esta es una mala implementación porque no está dirigida por eventos. Esperas una cantidad de tiempo determinada y solo esperas que haya sido lo suficientemente largo. Cuanto más espere, más seguro estará, pero también más tiempo perderá si no necesita esperar tanto. Tal vez alguien está en un dispositivo muy viejo / lento y este tiempo de espera no es suficiente. Desafortunadamente, estoy aquí porque los eventos no me proporcionan lo que necesito para determinar si se está haciendo clic en un iframe en Firefox, pero funciona para todos los demás navegadores. Estoy muy tentado a usar este método ahora, aunque es una mala práctica.
CTS_AE
1
Esta pregunta es bastante antigua, se remonta a 2008. Mientras tanto, puede haber mejores enfoques. ¿Puedes probar la respuesta de 2015?
Michiel Borkent
16

Es posible usar el evento mousedown del documento en lugar del desenfoque:

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

Las FocusEventinstancias de tipo tienen relatedTargetatributo, sin embargo, hasta la versión 47 del FF, específicamente, este atributo devuelve nulo, de 48 ya funciona.

Puedes ver más aquí .

rplaurindo
fuente
2

También estoy tratando de hacer que Autocompleter ignore el desenfoque si un elemento específico hizo clic y tiene una solución de trabajo, pero solo para Firefox debido al explícitoOriginalTarget

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

Este código envuelve el método onBlur predeterminado del Autocompletador y comprueba si se configuran los parámetros ignoreBlurEventElement. si está configurado, verifica cada vez para ver si el elemento en el que se hizo clic es ignoreBlurEventElement o no. Si es así, el Autocompletador no llama onBlur, sino que llama a onBlur. El único problema con esto es que solo funciona en Firefox porque la propiedad explícitaOriginalTarget es específica de Mozilla. Ahora estoy tratando de encontrar una forma diferente a usar explícitaOriginalTarget. La solución que ha mencionado requiere que agregue el comportamiento onclick manualmente al elemento. Si no puedo resolver el problema explícito de OriginalOriginalTarget, creo que seguiré tu solución.

mate
fuente
2

¿Puedes revertir lo que estás comprobando y cuándo? Eso es si recuerdas lo que fue borroso por última vez:

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

y luego en onClick para su lapso, llame a function () con ambos objetos:

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

Su función podría decidir si activar o no el control Ajax.AutoCompleter. La función tiene el objeto cliqueado y el objeto borroso. El onBlur ya ha sucedido, por lo que no hará desaparecer las sugerencias.

bmb
fuente
1

Usa algo como esto:

var myVar = null;

Y luego dentro de su función:

myVar = fldID;

Y entonces:

setTimeout(setFocus,1000)

Y entonces:

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

Código 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
fuente
1

Creo que no es posible, con IE puedes intentar usarlo window.event.toElement, ¡pero no funciona con Firefox!

stefano m
fuente
No funciona con Chrome o Safari. Tal vez no funcione con nuevas versiones de IE que sean más compatibles con los estándares.
robocat
1

Como se señaló en esta respuesta , puede verificar el valor de document.activeElement. documentes una variable global, por lo que no tiene que hacer ninguna magia para usarla en su controlador onBlur:

function myOnBlur(e) {
  if(document.activeElement ===
       document.getElementById('elementToCheckForFocus')) {
    // Focus went where we expected!
    // ...
  }
}
Kevin
fuente
1
  • document.activeElement podría ser un nodo primario (por ejemplo, el nodo del cuerpo porque está en una fase temporal cambiando de un objetivo a otro), por lo que no es utilizable para su alcance
  • ev.explicitOriginalTarget no siempre se valora

Entonces, la mejor manera es usar el evento onclick on body para comprender indirectamente que su nodo (event.target) está borroso


fuente
1

Funciona en Google Chrome v66.x, Mozilla v59.xy Microsoft Edge ... Solución con jQuery.

Pruebo en Internet Explorer 9 y no es compatible.

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

Comente su prueba en otras versiones de Internet Explorer.

LuisEduardox
fuente
0

Editar: Una forma hacky de hacerlo sería crear una variable que mantenga un seguimiento del enfoque para cada elemento que le interese. Entonces, si le importa que 'myInput' haya perdido el foco, establezca una variable en foco.

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

Respuesta original: puede pasar 'esto' a la función.

<input id="myInput" onblur="function(this){
   var theId = this.id; // will be 'myInput'
}" />
brock.holum
fuente
esto no responde la pregunta, espero haberlo aclarado con el ejemplo adicional
Michiel Borkent el
0

Sugiero usar variables globales blurfrom y blurto. Luego, configure todos los elementos que le interesan para asignar su posición en el DOM a la variable desenfoque de cuando pierden el foco. Además, configúrelos de modo que ganar foco establezca la variable en su posición en el DOM. Luego, podría usar otra función por completo para analizar el desenfoque y los datos blurto.

Stalepretzel
fuente
Es casi la solución, pero en el controlador de eventos onblur ya necesitaba saber en qué elemento se hizo clic, por lo que un tiempo de espera en el onblur lo hizo por mí.
Michiel Borkent
0

tenga en cuenta que la solución con explicitOriginalTarget no funciona para saltos de entrada de texto a entrada de texto.

intente reemplazar los botones con las siguientes entradas de texto y verá la diferencia:

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

fuente
0

He estado jugando con esta misma función y descubrí que FF, IE, Chrome y Opera tienen la capacidad de proporcionar el elemento fuente de un evento. No he probado Safari, pero supongo que podría tener algo 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
fuente
esto funciona bien por qué downvoting? simplemente elimine originalEvent!
fekiri malek
0

No me gusta usar el tiempo de espera al codificar JavaScript, por lo que lo haría de la manera opuesta a Michiel Borkent. (No probé el código, pero deberías tener la idea).

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

En la cabeza algo así

<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
fuente
0

Puedes arreglar IE con:

 event.currentTarget.firstChild.ownerDocument.activeElement

Parece "explícitoOriginalTarget" para FF.

Antoine y J

Madbean
fuente
0

Escribí una solución alternativa cómo hacer que cualquier elemento sea enfocable y "borrable".

Se basa en hacer un elemento como contentEditabley ocultarlo visualmente y deshabilitar el modo de edición en sí:

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

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

MANIFESTACIÓN

Nota: Probado en Chrome, Firefox y Safari (OS X). No estoy seguro acerca de IE.


Relacionado: Estaba buscando una solución para VueJs, así que para aquellos interesados ​​/ curiosos en cómo implementar dicha funcionalidad utilizando la directiva Vue Focusable, echen un vistazo .

Serhii Matrunchyk
fuente
-2

De esta manera:

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

O si adjunta el oyente a través de JavaScript (jQuery en este ejemplo):

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

Editar : lo siento. Leí mal la pregunta.

Armin Ronacher
fuente
3
Si sabe que esta no es la respuesta a la pregunta y la leyó mal, actualícela en consecuencia o elimínela.
TJ
-2


Creo que es fácilmente posible a través de jquery pasando la referencia del campo que causa el evento onblur en "this".
Por ej.

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

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

Gracias
monika

Monika Sharma
fuente
-2

Podrías hacerlo así:

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

Solo veo hacks en las respuestas, pero en realidad hay una solución integrada muy fácil de usar: Básicamente, puede capturar el elemento de enfoque de esta manera:

const focusedElement = document.activeElement

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

Thomas J.
fuente
Al menos en mi caso, esto no funciona ya que activeElement es el cuerpo, si verifico esto en el siguiente render / tick, está configurado correctamente. Pero parece que la solución de shog9 es la única solución adecuada, ya que se asegura de que una marca haya estado funcionando.
Mathijs Segers