¿Cómo averiguo qué elemento DOM tiene el foco?

1309

Me gustaría descubrir, en JavaScript, qué elemento tiene el foco actualmente. He estado buscando en el DOM y todavía no he encontrado lo que necesito. ¿Hay alguna manera de hacer esto y cómo?

La razón por la que estaba buscando esto:

Estoy tratando de hacer teclas como las flechas y enternavegar a través de una tabla de elementos de entrada. La pestaña funciona ahora, pero ingresa, y las flechas no lo hacen de manera predeterminada. Tengo configurada la parte de manejo de claves, pero ahora necesito descubrir cómo mover el foco en las funciones de manejo de eventos.

Tony Peterson
fuente
2
Aquí hay un bookmarklet que console.log el elemento con foco: github.com/lingtalfi/where-is-focus-bookmarklet
ling
Puede usar el find-focused-elementpaquete: npmjs.com/package/find-focused-element
Maxim Zhukov

Respuestas:

1533

Uso document.activeElement, es compatible con todos los principales navegadores.

Anteriormente, si intentaba averiguar qué campo de formulario tenía el foco, no podía. Para emular la detección en navegadores antiguos, agregue un controlador de eventos "focus" a todos los campos y registre el último campo enfocado en una variable. Agregue un controlador "desenfoque" para borrar la variable en un evento de desenfoque para el último campo enfocado.

Si necesita eliminar el activeElementpuede usar desenfoque; document.activeElement.blur(). Cambiará el activeElementa body.

Enlaces relacionados:

Dave Jarvis
fuente
53
No estoy seguro acerca de IE, pero FF y Safari devuelven el elemento BODY.
JW.
10
activeElementen realidad no devuelve el elemento enfocado. Cualquier elemento puede tener foco. Si un documento tiene 4 'scrolldivs', 0 o 1 de esos divs se puede desplazar con las teclas de flecha. Si hace clic en uno, ese div se enfoca. Si hace clic fuera de todo, el cuerpo está enfocado. ¿Cómo saber qué scrolldiv está enfocado? jsfiddle.net/rudiedirkx/bC5ke/show (ver consola)
Rudie
18
@Rudie, @Stewart: He construido sobre tu violín para crear un patio de juegos más elaborado: jsfiddle.net/mklement/72rTF . Descubrirá que el único navegador importante (a fines de 2012) que realmente puede enfocarse dives Firefox 17, y solo presionándolo . Los tipos de elementos a través de los cuales TODOS los principales navegadores regresan document.activeElementestán restringidos a elementos relacionados con la entrada . Si ningún elemento de este tipo tiene el foco, todos los navegadores principales devuelven el bodyelemento, excepto IE 9, que devuelve el htmlelemento.
mklement0
10
No estoy seguro de si ayuda, pero puede hacer que un elemento como un div reciba el foco del teclado al incluir el atributo tabindex = "0"
Marco Luglio
44
Cualquier acceso a document.activeElementdebe estar envuelto en un, try catchya que en algunas circunstancias puede arrojar una excepción (no solo IE9 AFAIK). Ver bugs.jquery.com/ticket/13393 y bugs.jqueryui.com/ticket/8443
robocat el
127

Como dijo JW, no puede encontrar el elemento enfocado actual, al menos de forma independiente del navegador. Pero si su aplicación es solo IE (algunas son ...), puede encontrarla de la siguiente manera:

document.activeElement

EDITAR: Parece que IE no tenía todo mal después de todo, esto es parte del borrador HTML5 y parece ser compatible con la última versión de Chrome, Safari y Firefox al menos.

Wookai
fuente
55
FF3 también. Esto es en realidad parte de la especificación HTML5 en torno a la "gestión de enfoque".
Crescent Fresh el
2
Funciona en la versión actual de Chrome y Opera (9.62). No funciona en Safari 3.2.3 en OS X, pero funciona en Safari 4 que fue lanzado ayer :)
gregers
sigue siendo el mismo para Chrome 19: S
Sebas
1
Solo funciona en Chrome (20) / Safari (5.1.3) cuando usa el teclado para tabular el elemento. Si hace clic en él, ni jquery: focus selector ni document.activeElement logran devolver lo que hizo clic (devolviendo elementos indefinidos y del cuerpo del documento respectivamente). PD: No puedo creer que este hilo tenga 2 años y todavía haya problemas de regresión en el kit web, junto con el que no funciona con los enlaces de omisión, pero se está haciendo mucho trabajo al agregar css3 experimental. Creo que puedo volver a recomendar Firefox a mi familia y amigos.
Amanecer
^^ document.activeElement está bien cuando hace clic para enfocarse en un área de texto u otra entrada. Si es un enlace, no se enfocará cuando se haga clic en él (en la página o, obviamente, de lo contrario).
marksyzm
84

Si puede usar jQuery, ahora es compatible con: focus, solo asegúrese de estar utilizando la versión 1.6+.

Esta declaración le dará el elemento actualmente enfocado.

$(":focus")

De: Cómo seleccionar un elemento que se centra en él con jQuery

William Denniss
fuente
44
Esto es bueno, pero ¿cómo lo hace jQuery? document.activeElement? Encontré esto: return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
Harry Pehkonen
46

document.activeElementahora es parte de la especificación del borrador de trabajo HTML5 , pero es posible que aún no sea compatible con algunos navegadores no principales / móviles / antiguos. Puede recurrir a querySelector(si eso es compatible). También vale la pena mencionar que document.activeElementvolverá document.bodysi ningún elemento está enfocado, incluso si la ventana del navegador no tiene el foco.

El siguiente código solucionará este problema y recurrirá a querySelectorbrindar un poco mejor soporte.

var focused = document.activeElement;
if (!focused || focused == document.body)
    focused = null;
else if (document.querySelector)
    focused = document.querySelector(":focus");

Una cosa adicional a tener en cuenta es la diferencia de rendimiento entre estos dos métodos. Consultar el documento con selectores siempre será mucho más lento que acceder a la activeElementpropiedad. Vea esta prueba de jsperf.com .

Andy E
fuente
21

Por sí solo, document.activeElementaún puede devolver un elemento si el documento no está enfocado (¡y por lo tanto, nada en el documento está enfocado!)

Es posible que desee ese comportamiento, o puede que no importe (por ejemplo, dentro de un keydownevento), pero si necesita saber que algo está realmente enfocado, también puede verificarlo document.hasFocus().

Lo siguiente le dará el elemento enfocado si hay uno, o si no null.

var focused_element = null;
if (
    document.hasFocus() &&
    document.activeElement !== document.body &&
    document.activeElement !== document.documentElement
) {
    focused_element = document.activeElement;
}

Para verificar si un elemento específico tiene foco, es más simple:

var input_focused = document.activeElement === input && document.hasFocus();

Para verificar si algo está enfocado, es más complejo nuevamente:

var anything_is_focused = (
    document.hasFocus() &&
    document.activeElement !== null &&
    document.activeElement !== document.body &&
    document.activeElement !== document.documentElement
);

Nota de robustez : en el código donde se compara document.bodyy document.documentElement, esto se debe a que algunos navegadores devuelven uno de estos o nullcuando nada está enfocado.

No tiene en cuenta si el <body>(o tal vez <html>) tenía un tabIndexatributo y, por lo tanto, podría enfocarse . Si está escribiendo una biblioteca o algo y quiere que sea robusta, probablemente debería manejar eso de alguna manera.


Aquí hay una versión de "un trazo" ( comillas aéreas pesadas ) para obtener el elemento enfocado, que es conceptualmente más complicado porque tienes que saber sobre el cortocircuito y, obviamente, no cabe en una línea, suponiendo que quiero que sea legible.
No voy a recomendar este. Pero si eres un 1337 hax0r, idk ... está ahí.
También puede eliminar la || nullpieza si no le importa falseen algunos casos. (Aún podría obtener nullsi document.activeElementes null):

var focused_element = (
    document.hasFocus() &&
    document.activeElement !== document.body &&
    document.activeElement !== document.documentElement &&
    document.activeElement
) || null;

Para verificar si un elemento específico está enfocado, alternativamente podría usar eventos, pero de esta manera requiere configuración (y potencialmente desmontaje), y lo que es más importante, asume un estado inicial :

var input_focused = false;
input.addEventListener("focus", function() {
    input_focused = true;
});
input.addEventListener("blur", function() {
    input_focused = false;
});

Puede corregir la suposición de estado inicial utilizando la forma no especificada, pero también podría usarla en su lugar.

1j01
fuente
15

document.activeElementpuede ser el <body>elemento predeterminado si no hay elementos enfocables enfocados. Además, si un elemento está enfocado y la ventana del navegador está borrosa, activeElementcontinuará manteniendo el elemento enfocado.

Si cualquiera de estos dos comportamientos no son deseables, considerar un enfoque basado en CSS: document.querySelector( ':focus' ).

Nate Whittaker
fuente
Genial, sí, en mi caso, tu enfoque tenía absolutamente sentido. Puedo configurar mis elementos enfocables con 'tabindex = "- 1"', si ninguno de ellos tiene foco (digamos, texto o imagen, que no me importa), el documento.querySelector (': foco') devuelve nulo.
Manfred
Vea mi respuesta para evitar usar querySelector: stackoverflow.com/a/40873560/2624876
1j01
10

Me gustó el enfoque utilizado por Joel S, pero también me encanta la simplicidad de document.activeElement. Usé jQuery y combiné los dos. Los navegadores más antiguos que no son compatibles document.activeElementlo usarán jQuery.data()para almacenar el valor de 'hasFocus'. Se utilizarán navegadores más nuevos document.activeElement. Supongo que document.activeElementtendrá un mejor rendimiento.

(function($) {
var settings;
$.fn.focusTracker = function(options) {
    settings = $.extend({}, $.focusTracker.defaults, options);

    if (!document.activeElement) {
        this.each(function() {
            var $this = $(this).data('hasFocus', false);

            $this.focus(function(event) {
                $this.data('hasFocus', true);
            });
            $this.blur(function(event) {
                $this.data('hasFocus', false);
            });
        });
    }
    return this;
};

$.fn.hasFocus = function() {
    if (this.length === 0) { return false; }
    if (document.activeElement) {
        return this.get(0) === document.activeElement;
    }
    return this.data('hasFocus');
};

$.focusTracker = {
    defaults: {
        context: 'body'
    },
    focusedElement: function(context) {
        var focused;
        if (!context) { context = settings.context; }
        if (document.activeElement) {
            if ($(document.activeElement).closest(context).length > 0) {
                focused = document.activeElement;
            }
        } else {
            $(':visible:enabled', context).each(function() {
                if ($(this).data('hasFocus')) {
                    focused = this;
                    return false;
                }
            });
        }
        return $(focused);
    }
};
})(jQuery);
Jason
fuente
3
¿Podría ser reemplazado por @William Denniss's $("*:focus")?
Pylinux
Supongo que podría. Escribí esto hace mucho tiempo y nunca tuve una razón para revisar una solución mejor ahora que es 5 años después. ¡Pruébalo! Yo podría hacer lo mismo. ¡Tengo menos complementos en nuestro sitio! :)
Jason
10

Un pequeño ayudante que he usado para estos propósitos en Mootools:

FocusTracker = {
    startFocusTracking: function() {
       this.store('hasFocus', false);
       this.addEvent('focus', function() { this.store('hasFocus', true); });
       this.addEvent('blur', function() { this.store('hasFocus', false); });
    },

    hasFocus: function() {
       return this.retrieve('hasFocus');
    }
}

Element.implement(FocusTracker);

De esta manera, puede verificar si el elemento tiene el foco el.hasFocus()siempre que startFocusTracking()se haya llamado al elemento dado.

Joel S
fuente
7

JQuery admite la :focuspseudoclase a partir de la corriente. Si lo está buscando en la documentación de JQuery, consulte "Selectores" donde le señala los documentos CSS del W3C . He probado con Chrome, FF e IE 7+. Tenga en cuenta que para que funcione en IE, <!DOCTYPE...debe existir en la página html. Aquí hay un ejemplo asumiendo que ha asignado una identificación al elemento que tiene foco:

$(":focus").each(function() {
  alert($(this).attr("id") + " has focus!");
});
DeezCashews
fuente
1
Debe (¿siempre?) Usar en this.idlugar de $(this).attr('id'), o al menos (cuando ya tiene su objeto jQuery) $(this)[0].id. Javascript nativo en este nivel es MUCHO más rápido y más eficiente. Puede no ser notable en este caso, pero en todo el sistema notará una diferencia.
Martijn
7

Si desea obtener un objeto de instancia Element, debe usarlo document.activeElement, pero si desea obtener un objeto de instancia Text, debe usarlo document.getSelection().focusNode.

Espero ayuda

rplaurindo
fuente
¿Mejor de qué manera?
1j01
Abra el inspector de su navegador, haga clic en cualquier lugar de la página, pase esto document.getSelection().focusNode.parentElementy toque Entrar. Después de eso, pasa document.activeElementy haz lo mismo. ;)
rplaurindo
Con este cuadro de comentarios enfocado, document.activeElementda el <textarea>while document.getSelection().focusNodeda el <td>que contiene el <textarea>(y document.getSelection().focusNode.parentElementda el que <tr>contiene el <td>)
1j01
Lo siento, mis disculpas. No lo expliqué bien. Si desea obtener un objeto de instancia Element, debe usarlo document.activeElement, pero si desea obtener un objeto de instancia Text, debe usarlo document.getSelection().focusNode. Por favor, pruébalo de nuevo. Espero haberte ayudado.
rplaurindo
2
La pregunta es qué elemento tiene foco actualmente. Y focusNodetampoco se garantiza que sea un nodo de texto.
1j01
6

Hay posibles problemas con el uso de document.activeElement. Considerar:

<div contentEditable="true">
  <div>Some text</div>
  <div>Some text</div>
  <div>Some text</div>
</div>

Si el usuario se centra en un div interno, document.activeElement todavía hace referencia al div externo. No puede usar document.activeElement para determinar cuál de los div internos tiene foco.

La siguiente función evita esto y devuelve el nodo enfocado:

function active_node(){
  return window.getSelection().anchorNode;
}

Si prefiere obtener el elemento enfocado, use:

function active_element(){
  var anchor = window.getSelection().anchorNode;
  if(anchor.nodeType == 3){
        return anchor.parentNode;
  }else if(anchor.nodeType == 1){
        return anchor;
  }
}
Nathan K
fuente
3
Eso no es realmente un problema con document.activeElement: los <div>elementos internos en realidad no pueden recibir el foco, como puede ver visualmente configurando la :focuspseudo-clase en algo visible (ejemplo: jsfiddle.net/4gasa1t2/1 ). De lo que estás hablando es de cuál de los <div>s internos contiene la selección o el cursor, que es un tema separado.
Tim Down
6

He encontrado que el siguiente fragmento es útil cuando intento determinar qué elemento tiene foco actualmente. Copie lo siguiente en la consola de su navegador, y cada segundo imprimirá los detalles del elemento actual que tiene el foco.

setInterval(function() { console.log(document.querySelector(":focus")); }, 1000);

Siéntase libre de modificar console.logpara cerrar sesión algo diferente para ayudarlo a identificar el elemento exacto si imprimir todo el elemento no lo ayuda a identificar el elemento.

vegemite4me
fuente
5

Al leer otras respuestas e intentarlo, parece que document.activeElementle dará el elemento que necesita en la mayoría de los navegadores.

Si tiene un navegador que no admite document.activeElement si tiene jQuery, debería poder completarlo en todos los eventos de enfoque con algo muy simple como esto (no probado, ya que no tengo un navegador que cumpla con esos criterios a mano) ):

if (typeof document.activeElement === 'undefined') { // Check browser doesn't do it anyway
  $('*').live('focus', function () { // Attach to all focus events using .live()
    document.activeElement = this; // Set activeElement to the element that has been focussed
  });
}
rjmunro
fuente
5

Si está usando jQuery, puede usar esto para averiguar si un elemento está activo:

$("input#id").is(":active");
Arne
fuente
4

Con dojo, puede usar dijit.getFocus ()

Daniel Hartmann
fuente
3

Solo pongo esto aquí para dar la solución que finalmente se me ocurrió.

Creé una propiedad llamada document.activeInputArea, y usé el complemento HotQeys de jQuery para atrapar eventos de teclado para las teclas de flecha, tabular e ingresar, y creé un controlador de eventos para hacer clic en los elementos de entrada.

Luego ajusté activeInputArea cada vez que cambiaba el foco, para poder usar esa propiedad para averiguar dónde estaba.

Sin embargo, es fácil arruinar esto, porque si tiene un error en el sistema y el foco no está donde cree que está, entonces es muy difícil restaurar el foco correcto.

Tony Peterson
fuente