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

1455

Me gustaría averiguar, 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 forma 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 ingrese, y parece que las flechas no lo hacen por defecto. Tengo configurada la parte de manejo de claves, pero ahora necesito descubrir cómo mover el foco en las funciones de manejo de eventos.

4
1709

Úselo document.activeElement, es compatible con todos los navegadores principales.

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

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

Enlaces relacionados:

14
  • 62
    No estoy seguro de IE, pero FF y Safari devuelven el elemento BODY.
    JW.
    2/07/10 a las 15:19
  • 13
    activeElementen realidad no devuelve el elemento enfocado. Cualquier elemento puede tener foco. Si un documento tiene 4 'scrolldivs', se puede desplazar por 0 o 1 de esos divs mediante las teclas de flecha. Si hace clic en uno, ese div está enfocado. 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
    22/10/12 a las 22:40
  • 24
    @Rudie, @Stewart: Me basé en tu violín para crear un campo de juego más elaborado: jsfiddle.net/mklement/72rTF . Descubrirá que el único navegador importante (a finales de 2012) que realmente puede enfocar tal dives Firefox 17, y solo al presionarlo . Los tipos de elementos a través de los cuales TODOS los navegadores principales devuelven 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. 22/12/12 a las 5:20
  • 12
    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" 12/04/13 a las 16:49
  • 6
    Cualquier acceso a document.activeElementdebe estar envuelto en un, try catchya que en algunas circunstancias puede generar una excepción (no solo IE9 AFAIK). Consulte bugs.jquery.com/ticket/13393 y bugs.jqueryui.com/ticket/8443
    robocat
    2 de julio de 2013 a las 2:11
136

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

document.activeElement

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

7
  • 5
    FF3 también. En realidad, esto es parte de la especificación HTML5 en torno a la "gestión del enfoque". 31 de enero de 2009 a las 0:37
  • 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 se lanzó ayer :)
    gregers
    9 de junio de 2009 a las 15:02
  • sigue igual para Chrome 19: S
    Sebas
    20/06/12 a las 23:12
  • 1
    Solo funciona en chrome (20) / safari (5.1.3) cuando usas el teclado para tabular el elemento. Si hace clic en él, ni el selector de jquery: focus ni el document.activeElement logran devolver el elemento en el que hizo clic (devolviendo el elemento undefined y el cuerpo del documento respectivamente). PD: No puedo creer que este hilo tenga 2 años y todavía hay problemas de regresión en webkit, junto con uno en el que los enlaces de omisión no funcionan, pero se está trabajando mucho para agregar css3 experimental. Creo que puedo volver a recomendar Firefox a mi familia y amigos.
    Dawn
    5 de julio de 2012 a las 11:03
  • 1
    Devuelve 'cuerpo' para la mayoría de los elementos. Bastante inútil. 21/03/20 a las 12:46
90

Si puede usar jQuery, ahora admite: focus, solo asegúrese de estar usando 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

1
  • 5
    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); 13/10/2017 a las 3:16
48

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

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

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 .

0
34

Por sí mismo, 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 lo hay, 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 comprueba document.bodyy document.documentElement, esto se debe a que algunos navegadores devuelven uno de estos o nullcuando no hay nada enfocado.

No tiene en cuenta si <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 robusto, probablemente debería manejar eso de alguna manera.


Aquí hay una ( pesados airquotes) "de una sola línea" versión de conseguir el elemento enfocado, que es conceptualmente más complicado porque hay que saber acerca de cortocircuitos, y ya sabes, es obvio que no cabe en una sola línea, suponiendo que quiere que sea legible.
No voy a recomendar este. Pero si eres un 1337 hax0r, idk ... está ahí.
También puede quitar la || nullpieza si no le importa conseguirla 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 puede 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;
});

Podrías arreglar la suposición del estado inicial usando la forma sin eventos, pero entonces también podrías usar eso en su lugar.

19

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

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

2
  • Genial, sí, en mi caso, tu enfoque tenía mucho sentido. Puedo configurar mis elementos enfocables con 'tabindex = "- 1"', si ninguno de ellos tiene foco (digamos, algún texto o imagen, que no me importa) el document.querySelector (': focus') devuelve nulo.
    Manfred
    10 de mayo de 2015 a las 13:49
  • Vea mi respuesta para evitar usar querySelector: stackoverflow.com/a/40873560/2624876
    1j01
    29/11/2016 a las 19:12
12

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 en el elemento dado.

12

He encontrado que el siguiente fragmento es útil cuando intento determinar qué elemento tiene el 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 el console.logpara cerrar sesión en algo diferente que lo ayude a identificar el elemento exacto si imprimir todo el elemento no lo ayuda a identificar el elemento.

11

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.activeElementse utilizarán jQuery.data()para almacenar el valor de 'hasFocus'. Los navegadores más nuevos utilizarán 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);
2
  • 3
    ¿Podría ser reemplazado por el de @William Denniss $("*:focus")?
    Pylinux
    30 jul.15 a las 10:30
  • Supongo que podría. Escribí esto hace mucho tiempo y nunca tuve una razón para revisar una solución mejor ahora que ha pasado 5 años. ¡Pruébalo! Podría hacer lo mismo. ¡Tengo menos plugin en nuestro sitio! :)
    Jason
    21/08/15 a las 19:33
9

JQuery admite la :focuspseudoclase a partir de la actualidad. Si lo está buscando en la documentación de JQuery, consulte en "Selectores", donde le indica los documentos CSS del W3C . Probé 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 el foco:

$(":focus").each(function() {
  alert($(this).attr("id") + " has focus!");
});
1
  • 1
    Debería (¿siempre?) Usar en this.idlugar de $(this).attr('id'), o al menos (cuando ya tenga su objeto jQuery) $(this)[0].id. Javascript nativo en este nivel es MUCHO más rápido y más eficiente. Es posible que no se note en este caso, pero en todo el sistema notará una diferencia.
    Martijn
    15/12/15 a las 13:51
9

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

Espero ayude.

11
  • ¿Mejor de qué manera?
    1j01
    29/11/2016 a las 19:10
  • Abra el inspector de su navegador, haga clic en cualquier lugar de la página, pase este document.getSelection().focusNode.parentElementy toque Entrar. Después de eso, pasa document.activeElementy haz lo mismo. ;) 29/11/2016 a las 19:22
  • Con este cuadro de comentarios enfocado, document.activeElementda el <textarea>considerando document.getSelection().focusNodeda el <td>que contiene el <textarea>(y document.getSelection().focusNode.parentElementda el que <tr>contiene el <td>)
    1j01
    1 de diciembre de 2016 a las 2:37
  • Lo siento, mis disculpas. No lo expliqué bien. Si desea obtener un objeto que es una instancia de Element, debe usar document.activeElement, pero si desea obtener un objeto que es una instancia de Text, debe usar document.getSelection().focusNode. Por favor, pruébelo de nuevo. Espero haber ayudado. 1 de diciembre de 2016 a las 3:53
  • 2
    La pregunta es en qué elemento se está enfocando actualmente. Y focusNodetampoco se garantiza que sea un nodo de texto.
    1j01
    2 de diciembre de 2016 a las 6:45
7

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

$("input#id").is(":active");
7

Existen problemas potenciales 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 el 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;
  }
}
1
  • 3
    Eso no es realmente un problema con document.activeElement: los <div>elementos internos en realidad no pueden recibir el enfoque, como puede ver visualmente configurando la pseudoclase:focus en algo visible (ejemplo: jsfiddle.net/4gasa1t2/1 ). De lo que estás hablando es de cuál de las <div>s internas contiene la selección o el signo de intercalación, que es un tema aparte. 19/07/2015 a las 9:59
6

Al leer otras respuestas y probarlo yo mismo, parece que document.activeElementle dará el elemento que necesita en la mayoría de los navegadores.

Si tiene un navegador que no es compatible con 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
  });
}
5

Con dojo, puedes usar dijit.getFocus ()

4

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

Creé una propiedad llamada document.activeInputArea, y usé el complemento HotKeys de jQuery para atrapar eventos de teclado para teclas de flecha, tabulación e ingresar, y creé un controlador de eventos para hacer clic en elementos de entrada.

Luego ajusté el activeInputArea cada vez que cambiaba el enfoque, por lo que podía usar esa propiedad para averiguar dónde estaba.

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