Come faccio a sapere quale elemento DOM ha lo stato attivo?


1309

Vorrei scoprire, in JavaScript, quale elemento è attualmente focalizzato. Ho controllato il DOM e non ho ancora trovato ciò di cui ho bisogno. C'è un modo per farlo, e come?

Il motivo per cui stavo cercando questo:

Sto cercando di creare tasti come le frecce e enternavigare attraverso una tabella di elementi di input. La scheda funziona ora, ma invio e le frecce non lo fanno per impostazione predefinita. Ho impostato la parte di gestione delle chiavi, ma ora devo capire come spostare il focus nelle funzioni di gestione degli eventi.


2
Ecco un bookmarklet che console.log l'elemento in primo piano: github.com/lingtalfi/where-is-focus-bookmarklet
ling

Puoi usare il find-focused-elementpacchetto: npmjs.com/package/find-focused-element
Maxim Zhukov

Risposte:


1533

Usa document.activeElement, è supportato in tutti i principali browser.

In precedenza, se stavi cercando di scoprire quale campo del modulo ha lo stato attivo, non potresti. Per emulare il rilevamento nei browser meno recenti, aggiungi un gestore eventi "attivo" a tutti i campi e registra l'ultimo campo attivo in una variabile. Aggiungi un gestore "sfocatura" per cancellare la variabile su un evento sfocatura per l'ultimo campo focalizzato.

Se è necessario rimuovere il activeElementè possibile utilizzare la sfocatura; document.activeElement.blur(). Cambierà la activeElementa body.

Link correlati:


53
Non sono sicuro di IE, ma FF e Safari restituiscono entrambi l'elemento BODY.
JW.

10
activeElementin realtà non restituisce l'elemento focalizzato. Qualsiasi elemento può avere un focus. Se un documento ha 4 "scrolldivs", 0 o 1 di quei div è scorrevole con i tasti freccia. Se fai clic su uno, quel div è focalizzato. Se fai clic al di fuori di tutto, il corpo è focalizzato. Come scopri quale focusdiv è focalizzato? jsfiddle.net/rudiedirkx/bC5ke/show (controlla console)
Rudie,

18
@Rudie, @Stewart: ho costruito il tuo violino per creare un parco giochi più elaborato: jsfiddle.net/mklement/72rTF . Scoprirai che l'unico browser principale (alla fine del 2012) che può effettivamente focalizzare questo tipo divè Firefox 17, e solo eseguendo la tabulazione . I tipi di elementi restituiti da TUTTI i principali browser document.activeElementsono limitati agli elementi relativi all'input . Se tale elemento non ha lo stato attivo, tutti i principali browser restituiscono l' bodyelemento, tranne IE 9, che restituisce l' htmlelemento.
mklement0

10
Non sono sicuro che sia d'aiuto, ma puoi fare in modo che un elemento come un div riceva lo stato attivo della tastiera includendo l'attributo tabindex = "0"
Marco Luglio,

4
Qualsiasi accesso a document.activeElementdovrebbe essere racchiuso in un try catchdato che in alcune circostanze può generare un'eccezione (non solo IE9 AFAIK). Vedi bugs.jquery.com/ticket/13393 e bugs.jqueryui.com/ticket/8443
robocat

127

Come affermato da JW, non è possibile trovare l'elemento focalizzato corrente, almeno in modo indipendente dal browser. Ma se la tua app è solo IE (alcuni sono ...), puoi trovarla nel modo seguente:

document.activeElement

EDIT: Sembra che IE non abbia avuto tutto sbagliato dopo tutto, questo fa parte della bozza HTML5 e sembra essere supportato almeno dall'ultima versione di Chrome, Safari e Firefox.


5
Anche FF3. Questo in realtà fa parte delle specifiche HTML5 relative alla "gestione del focus".
Crescent Fresh,

2
Funziona con l'attuale versione di Chrome e Opera (9.62). Non funziona in Safari 3.2.3 su OS X, ma funziona in Safari 4 che è stato rilasciato ieri :)
gregers

sempre lo stesso per Chrome 19: S
Sebas,

1
Funziona solo in chrome (20) / safari (5.1.3) quando si utilizza la tastiera per accedere all'elemento. Se fai clic su di esso, allora né jquery: focus selector né document.activeElement riescono a restituire ciò su cui hai fatto clic (restituendo rispettivamente undefined e un elemento body del documento). PS Non posso credere che questo thread abbia 2 anni e ci sono ancora problemi di regressione sul webkit, insieme a quello in cui i skip link non funzionano, ma si sta facendo molto lavoro con l'aggiunta di css3 sperimentale. Penso che potrei tornare a raccomandare Firefox alla mia famiglia e ai miei amici.
Dawn,

^^ document.activeElement va bene quando fai clic per concentrarti su un'area di testo o altro input. Se si tratta di un collegamento che non si concentrerà quando si fa clic su (nella pagina o ovviamente in altro modo).
Markyzm,

84

Se puoi usare jQuery, ora supporta: focus, assicurati di utilizzare la versione 1.6+.

Questa affermazione ti fornirà l'elemento attualmente focalizzato.

$(":focus")

Da: Come selezionare un elemento che si concentra su di esso con jQuery


4
Questo va bene, ma come fa jQuery? document.activeElement? Ho trovato questo: return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
Harry Pehkonen il

46

document.activeElementfa ora parte delle specifiche della bozza di lavoro HTML5 , ma potrebbe non essere ancora supportato in alcuni browser non principali / mobili / meno recenti. Puoi tornare a querySelector(se supportato). Vale anche la pena ricordare che document.activeElementtornerà document.bodyse nessun elemento è attivo - anche se la finestra del browser non ha lo stato attivo.

Il codice seguente risolverà questo problema e tornerà a querySelectorfornire un supporto leggermente migliore.

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

Un'ulteriore cosa da notare è la differenza di prestazioni tra questi due metodi. L'interrogazione del documento con i selettori sarà sempre molto più lenta dell'accesso alla activeElementproprietà. Vedi questo test jsperf.com .


21

Di per sé, document.activeElementpuò comunque restituire un elemento se il documento non è focalizzato (e quindi nulla nel documento è focalizzato!)

Si può desiderare che il comportamento, oppure inoltre possibile, non importa (ad esempio all'interno di un keydownevento), ma se avete bisogno di sapere qualcosa in realtà è focalizzato, è possibile inoltre controllare document.hasFocus().

Quanto segue ti darà l'elemento focalizzato se ce n'è uno, altrimenti null.

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

Per verificare se un elemento specifico ha lo stato attivo, è più semplice:

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

Per verificare se qualcosa è focalizzato, è di nuovo più complesso:

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

Nota di robustezza : nel codice in cui viene verificato document.bodye document.documentElement, ciò è dovuto al fatto che alcuni browser restituiscono uno di questi o nullquando nulla è focalizzato.

Non tiene conto se il <body>(o forse<html> ) aveva un tabIndexattributo e quindi poteva effettivamente essere focalizzato . Se stai scrivendo una libreria o qualcosa del genere e vuoi che sia robusto, probabilmente dovresti gestirlo in qualche modo.


Ecco una versione "one-liner" ( airquotes pesanti ) per ottenere l'elemento focalizzato, che è concettualmente più complicata perché devi conoscere i cortocircuiti e, sai, ovviamente non si adatta su una linea, supponendo che tu voglio che sia leggibile.
Non consiglierò questo. Ma se sei un 1337 hax0r, idk ... è lì.
Potresti anche rimuovere la || nullparte se non ti dispiace ottenere falsein alcuni casi. (Potresti ancora ottenere nullsedocument.activeElement è null):

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

Per verificare se un elemento specifico è focalizzato, in alternativa è possibile utilizzare gli eventi, ma in questo modo è necessaria l'installazione (e potenzialmente lo smontaggio) e, soprattutto, assume uno stato iniziale :

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

È possibile correggere il presupposto dello stato iniziale utilizzando il modo non-evented, ma è possibile utilizzare tale invece.


15

document.activeElementpuò essere predefinito <body>sull'elemento se non sono presenti elementi a fuoco. Inoltre, se un elemento è focalizzato e la finestra del browser è sfocata,activeElement continuerà a trattenere l'elemento focalizzato.

Se uno di questi due comportamenti non sono desiderabili, prendere in considerazione un approccio basato su CSS: document.querySelector( ':focus' ).


Fantastico, sì, nel mio caso il tuo approccio ha avuto un senso assoluto. Posso impostare i miei elementi attivabili con 'tabindex = "- 1"', se nessuno di essi ha lo stato attivo (diciamo, del testo o delle immagini, di cui non mi interessa) il document.querySelector (': focus') restituisce nullo.
Manfred,

Vedere la mia risposta a evitare l'uso querySelector: stackoverflow.com/a/40873560/2624876
1j01

10

Mi è piaciuto l'approccio utilizzato da Joel S, ma amo anche la semplicità di document.activeElement. Ho usato jQuery e ho combinato i due. I browser meno recenti che non supportano document.activeElementutilizzeranno jQuery.data()per memorizzare il valore di "hasFocus". Verranno utilizzati i browser più recenti document.activeElement. Presumo che document.activeElementavrà prestazioni migliori.

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

3
Questo potrebbe essere sostituito da @William Denniss's $("*:focus")?
Pylinux,

Suppongo che potrebbe. L'ho scritto molto tempo fa e non ho mai avuto motivo di rivisitare una soluzione migliore ora che sono trascorsi 5 anni. Provalo! Potrei fare lo stesso. Ho meno plugin sul nostro sito! :)
Jason,

10

Un piccolo aiuto che ho usato per questi scopi in 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);

In questo modo è possibile verificare se l'elemento ha lo stato attivo a el.hasFocus()condizione che startFocusTracking()sia stato chiamato sull'elemento specificato.


7

JQuery supporta la :focuspseudo-classe attuale. Se lo stai cercando nella documentazione di JQuery, controlla in "Selettori" dove rimanda ai documenti CSS del W3C . Ho testato con Chrome, FF e IE 7+. Si noti che per funzionare in IE, <!DOCTYPE...deve esistere nella pagina HTML. Ecco un esempio supponendo che tu abbia assegnato un id all'elemento che ha il focus:

$(":focus").each(function() {
  alert($(this).attr("id") + " has focus!");
});

1
Dovresti (sempre?) Usare this.idinvece di $(this).attr('id'), o almeno (quando hai già il tuo oggetto jQuery) $(this)[0].id. Javascript nativo a questo livello è MODO più veloce ed efficiente. Potrebbe non essere evidente in questo caso, ma a livello di sistema noterai una differenza.
Martijn,

7

Se si desidera ottenere un oggetto di istanza Element, è necessario utilizzare document.activeElement, ma se si desidera ottenere un oggetto di istanza Text, è necessario utilizzare document.getSelection().focusNode.

Spero aiuti


Meglio in che modo?
1o gennaio

Apri la finestra di ispezione del browser, fai clic su un punto qualsiasi della pagina, oltre questo document.getSelection().focusNode.parentElemente tocca Invio. Dopodiché, passa document.activeElemente fallo samething. ;)
rplaurindo

Con questa casella di commento focalizzata, document.activeElementdà il <textarea>considerando che document.getSelection().focusNodedà il <td>che contiene il <textarea>(e document.getSelection().focusNode.parentElementdà il <tr>contenimento del <td>)
1j01

Scusate le mie scuse. Non l'ho spiegato bene. Se si desidera ottenere un oggetto che è istanza di Element, è necessario utilizzare document.activeElement, ma se si desidera ottenere un oggetto che è istanza di Text, è necessario utilizzare document.getSelection().focusNode. Per favore, prova di nuovo. Spero di aver aiutato.
rplaurindo,

2
La domanda è: quale elemento è attualmente focalizzato. E focusNodenon è nemmeno garantito che sia un nodo di testo.
1j01

6

Esistono potenziali problemi con l'utilizzo di document.activeElement. Prendere in considerazione:

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

Se l'utente si concentra su un div interno, document.activeElement fa ancora riferimento al div esterno. Non è possibile utilizzare document.activeElement per determinare quale dei div interni ha lo stato attivo.

La seguente funzione aggira questo e restituisce il nodo focalizzato:

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

Se preferisci ottenere l'elemento focalizzato, usa:

function active_element(){
  var anchor = window.getSelection().anchorNode;
  if(anchor.nodeType == 3){
        return anchor.parentNode;
  }else if(anchor.nodeType == 1){
        return anchor;
  }
}

3
Questo non è un problema document.activeElement: gli <div>elementi interni in realtà non possono ricevere il focus, come puoi vedere visivamente impostando la :focuspseudo-classe su qualcosa di visibile (esempio: jsfiddle.net/4gasa1t2/1 ). Quello di cui stai parlando è quale delle <div>s interne contiene la selezione o il cursore, che è un problema separato.
Tim Down,

6

Ho trovato utile il seguente frammento quando si cerca di determinare quale elemento è attualmente attivo. Copia quanto segue nella console del tuo browser e ogni secondo stamperà i dettagli dell'elemento corrente che è attivo.

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

Sentiti libero di modificare console.logper disconnettere qualcosa di diverso per aiutarti a individuare l'elemento esatto se stampare l'intero elemento non ti aiuta a individuare l'elemento.


5

Leggendo altre risposte e provandomi, sembra che document.activeElementti darà l'elemento di cui hai bisogno nella maggior parte dei browser.

Se hai un browser che non supporta document.activeElement se hai jQuery in giro, dovresti essere in grado di popolarlo su tutti gli eventi focus con qualcosa di molto semplice come questo (non testato poiché non ho un browser che soddisfi questi criteri a portata di 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

Se stai usando jQuery, puoi usarlo per scoprire se un elemento è attivo:

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


3

Metto questo qui per dare la soluzione che alla fine ho trovato.

Ho creato una proprietà chiamata document.activeInputArea e ho usato l'addon HotKeys di jQuery per intercettare gli eventi della tastiera per i tasti freccia, la scheda e l'invio e ho creato un gestore eventi per fare clic negli elementi di input.

Quindi ho modificato activeInputArea ogni volta che lo stato attivo è cambiato, in modo da poter utilizzare quella proprietà per scoprire dove mi trovassi.

Tuttavia, è facile rovinare tutto, perché se hai un bug nel sistema e la messa a fuoco non è dove pensi che sia, allora è molto difficile ripristinare la messa a fuoco corretta.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.