Usando jQuery para probar si una entrada tiene foco

561

En la página principal de un sitio que estoy construyendo, varios <div>usan la pseudoclase CSS :hoverpara agregar un borde cuando el mouse está sobre ellos. Una de las <div>s contiene una <form>que, usando jQuery, mantendrá el borde si una entrada dentro tiene foco. Esto funciona perfectamente, excepto que IE6 no admite :hoverningún elemento que no sea <a>s. Entonces, para este navegador solo estamos usando jQuery para imitar CSS :hoverusando el $(#element).hover()método. El único problema es que ahora que jQuery maneja tanto el formulario focus() y hover() , cuando una entrada tiene el foco, el usuario mueve el mouse hacia adentro y hacia afuera, el borde desaparece.

Estaba pensando que podríamos usar algún tipo de condicional para detener este comportamiento. Por ejemplo, si probamos con el mouse fuera si alguna de las entradas tenía foco, podríamos evitar que el borde desaparezca. AFAIK, no hay un :focusselector en jQuery, así que no estoy seguro de cómo hacer que esto suceda. ¿Algunas ideas?

leche azul
fuente
1
¿Puedes intentar reducirlo a unas pocas oraciones de lo que se requiere y de lo que estoy sucediendo?
mkoryak
Creo que debe considerar capturar el foco y desenfocar eventos ( docs.jquery.com/Events/focus y docs.jquery.com/Events/blur )
Wolfr
Publiqué una respuesta a la pregunta relacionada "¿Hay un 'foco' en JavaScript (o jQuery)?" . He mejorado [enfoque cletus citado por gnarf] (# 2684561).
luiggitama

Respuestas:

928

jQuery 1.6+

jQuery agregó un :focusselector para que ya no necesitemos agregarlo nosotros mismos. Solo usa$("..").is(":focus")

jQuery 1.5 y abajo

Editar: a medida que cambian los tiempos, encontramos mejores métodos para probar el enfoque, el nuevo favorito es esta esencia de Ben Alman :

jQuery.expr[':'].focus = function( elem ) {
  return elem === document.activeElement && ( elem.type || elem.href );
};

Citado de Mathias Bynens aquí :

Tenga en cuenta que la (elem.type || elem.href)prueba se agregó para filtrar falsos positivos como el cuerpo. De esta manera, nos aseguramos de filtrar todos los elementos, excepto los controles de formulario y los hipervínculos.

Estás definiendo un nuevo selector. Ver Complementos / Autoría . Entonces puedes hacer:

if ($("...").is(":focus")) {
  ...
}

o:

$("input:focus").doStuff();

Cualquier jQuery

Si solo desea averiguar qué elemento tiene foco, puede usar

$(document.activeElement)

Si no está seguro de si la versión será 1.6 o inferior, puede agregar el :focusselector si falta:

(function ( $ ) {
    var filters = $.expr[":"];
    if ( !filters.focus ) { 
        filters.focus = function( elem ) {
           return elem === document.activeElement && ( elem.type || elem.href );
        };
    }
})( jQuery );
gnarf
fuente
44
Esto puede causar falsos positivos. Consulte stackoverflow.com/questions/967096/… para obtener más información (gracias a Ben Alman y Diego Perini).
Mathias Bynens
@Mathias Bynens - Gracias por la actualización (eres más que bienvenido para editar el wiki respuesta de la comunidad por cierto!)
gnarf
¿Qué pasa cuando lo necesitas para trabajar con 1.5 y 1.6? No quiero anular el propio selector de enfoque de jQuery. Algo como if (!jQuery.expr[':'].focus) { /* gist from Ben Alman */ }?
Betamos
@betamos - Exactamente correcto ... Agregaré algo en la respuesta para reflejar eso.
Gnarf
2
@Alex: proporcione un caso de prueba reducido en jsFiddle que muestre el problema, porque hasta donde puedo decir, no hay ninguna razón para que esto no funcione en elementos "dinámicos". Llamo travesuras ...
Gnarf
46

CSS:

.focus {
    border-color:red;
}

JQuery:

  $(document).ready(function() {

    $('input').blur(function() {
        $('input').removeClass("focus");
      })
      .focus(function() {
        $(this).addClass("focus")
      });
  });
Daniel Moura
fuente
22

Aquí hay una respuesta más sólida que la actualmente aceptada:

jQuery.expr[':'].focus = function(elem) {
  return elem === document.activeElement && (elem.type || elem.href);
};

Tenga en cuenta que la (elem.type || elem.href)prueba se agregó para filtrar falsos positivos como body. De esta manera, nos aseguramos de filtrar todos los elementos, excepto los controles de formulario y los hipervínculos.

(Tomado de esta esencia por Ben Alman ).

Mathias Bynens
fuente
13

Actualización de abril de 2015

Dado que esta pregunta ha existido desde hace un tiempo, y algunas convenciones nuevas han entrado en juego, creo que debería mencionar que el .livemétodo se ha depreciado.

En su lugar, .onahora se ha introducido el método.

Su documentación es bastante útil para explicar cómo funciona;

El método .on () adjunta controladores de eventos al conjunto de elementos actualmente seleccionado en el objeto jQuery. A partir de jQuery 1.7, el método .on () proporciona toda la funcionalidad necesaria para adjuntar controladores de eventos. Para obtener ayuda en la conversión de métodos de eventos jQuery anteriores, consulte .bind (), .delegate () y .live ().

Entonces, para que pueda apuntar al evento 'enfocado en la entrada', puede usar esto en un script. Algo como:

$('input').on("focus", function(){
   //do some stuff
});

Esto es bastante robusto e incluso le permite usar la tecla TAB también.

jbutler483
fuente
2

No estoy completamente seguro de lo que está buscando, pero parece que se puede lograr almacenando el estado de los elementos de entrada (¿o el div?) Como una variable:

$('div').each(function(){

    var childInputHasFocus = false;

    $(this).hover(function(){
        if (childInputHasFocus) {
            // do something
        } else { }
    }, function() {
        if (childInputHasFocus) {
            // do something
        } else { }
    });

    $('input', this)
        .focus(function(){
            childInputHasFocus = true;
        })
        .blur(function(){
            childInputHasFocus = false;
        });
});
James
fuente
Esto es lo que terminé haciendo, pero usé una clase CSS arbitraria en lugar de una variable global de JavaScript.
bloudermilk
Usé una variación de esto. No se necesita un nuevo complemento jQuery. ¡Eso me hace una caravana feliz!
Dennis Day
1

Una alternativa al uso de clases para marcar el estado de un elemento es la funcionalidad interna del almacén de datos .

PD: Puedes almacenar booleanos y lo que quieras usando la data()función. No se trata solo de cadenas :)

$("...").mouseover(function ()
{
    // store state on element
}).mouseout(function ()
{
    // remove stored state on element
});

Y luego es solo cuestión de acceder al estado de los elementos.

cllpse
fuente
1

¿Has pensado en usar mouseOver y mouseOut para simular esto? También mire mouseEnter y mouseLeave

mkoryak
fuente
Eso es esencialmente lo que estoy haciendo con el hover de jQuery. Proporciona dos funciones, una para el mouse y otra para el mouse.
bloudermilk
0

Realice un seguimiento de ambos estados (suspendido, enfocado) como indicadores de verdadero / falso, y cada vez que uno cambie, ejecute una función que elimine el borde si ambos son falsos, de lo contrario, muestra el borde.

Entonces: conjuntos de enfoque enfocados = verdadero, conjuntos de desenfoque enfocados = falso. onmouseover establece hovered = true, onmouseout establece hovered = false. Después de cada uno de estos eventos, ejecute una función que agrega / elimina borde.

Blixt
fuente
0

Hasta donde yo sé, no puede preguntarle al navegador si alguna entrada en la pantalla tiene foco, debe configurar algún tipo de seguimiento de foco.

Por lo general, tengo una variable llamada "noFocus" y la configuro como verdadera. Luego agrego un evento de enfoque a todas las entradas que hace que noFocus sea falso. Luego agrego un evento de desenfoque a todas las entradas que vuelven a establecer noFocus a verdadero.

Tengo una clase MooTools que maneja esto con bastante facilidad, estoy seguro de que podría crear un complemento jquery para hacer lo mismo.

Una vez que se haya creado, puede verificar noFocus antes de realizar cualquier intercambio de bordes.

Ryan Florence
fuente
0

Hay un complemento para verificar si un elemento está enfocado: http://plugins.jquery.com/project/focused

$('input').each(function(){
   if ($(this) == $.focused()) {
      $(this).addClass('focused');
   }
})
Murat Çorlu
fuente
1
¿Por qué usar un complemento cuando solo puedes usar Core JQuery?
Marcy Sutton
1
Tienes razón. Pero no conocía una solución para este problema hace 2 años.
Murat Çorlu
0

Tuve un evento .live ("foco") configurado para seleccionar () (resaltar) el contenido de una entrada de texto para que el usuario no tuviera que seleccionarlo antes de escribir un nuevo valor.

$ (formObj) .select ();

Debido a las peculiaridades entre los diferentes navegadores, la selección a veces sería reemplazada por el clic que lo causó, y anularía la selección del contenido inmediatamente después a favor de colocar el cursor dentro del campo de texto (funcionó principalmente bien en FF pero falló en IE)

Pensé que podría resolver esto poniendo un ligero retraso en la selección ...

setTimeout (function () {$ (formObj) .select ();}, 200);

Esto funcionó bien y la selección persistiría, pero surgió un problema extraño. Si tabulaba de un campo al siguiente, el foco cambiaría al siguiente campo antes de que se realizara la selección. Dado que select roba el foco, el foco volverá y desencadenará un nuevo evento de "foco". Esto terminó en una cascada de selecciones de entrada bailando por toda la pantalla.

Una solución viable sería verificar que el campo aún tenga el foco antes de ejecutar select (), pero como se mencionó, no hay una manera simple de verificar ... Terminé simplemente prescindiendo de todo el resaltado automático, en lugar de activar lo que debería ser una sola llamada jQuery select () a una gran función cargada de subrutinas ...

Brian
fuente
0

Lo que terminé haciendo es crear una clase arbitraria llamada .elementhasfocus que se agrega y elimina dentro de la función jQuery focus (). Cuando la función hover () se ejecuta con el mouse fuera, busca .elementhasfocus:

if(!$("#quotebox").is(".boxhasfocus")) $(this).removeClass("box_border");

Entonces, si no tiene esa clase (léase: ningún elemento dentro del div tiene foco), se elimina el borde. De lo contrario, no pasa nada.

leche azul
fuente
-2

Simple

 <input type="text" /> 



 <script>
     $("input").focusin(function() {

    alert("I am in Focus");

     });
 </script>
Code Spy
fuente
Eso no te dice qué elemento está actualmente en foco. Se llama cuando un nuevo elemento obtiene el foco. Entonces, si un elemento ya tiene el foco y desea saber si 'este' es el indicado, este código es inútil.
Alexis Wilke