Jak dowiedzieć się, który element DOM ma fokus?

1309

Chciałbym dowiedzieć się, w JavaScript, który element jest obecnie aktywny. Przeglądałem DOM i nie znalazłem jeszcze tego, czego potrzebuję. Czy jest na to sposób i jak to zrobić?

Powodem, dla którego tego szukałem:

Staram się tworzyć klucze takie jak strzałki i enternawigować w tabeli elementów wejściowych. Tab działa teraz, ale Enter, a strzałki domyślnie nie wydają się. Mam skonfigurowaną część do obsługi klucza, ale teraz muszę wymyślić, jak przenieść fokus w funkcjach obsługi zdarzeń.

Tony Peterson
źródło
2
Oto bookmarklet z konsolą. Zaloguj
ling
Możesz użyć find-focused-elementpakietu: npmjs.com/package/find-focused-element
Maxim Zhukov

Odpowiedzi:

1533

Użyj document.activeElement, jest obsługiwany we wszystkich głównych przeglądarkach.

Poprzednio, jeśli próbowałeś dowiedzieć się, na czym skupia się pole formularza, nie mogłeś. Aby emulować wykrywanie w starszych przeglądarkach, dodaj moduł obsługi zdarzeń „focus” do wszystkich pól i zapisz pole, na którym ostatnio ustawiono ostrość, w zmiennej. Dodaj moduł obsługi „rozmycia”, aby wyczyścić zmienną po zdarzeniu rozmycia dla pola, na którym ostatnio ustawiono ostrość.

Jeśli chcesz usunąć activeElement, możesz użyć rozmycia; document.activeElement.blur(). Zmieni to activeElementna body.

Powiązane linki:

Dave Jarvis
źródło
53
Nie jestem pewien co do IE, ale zarówno FF, jak i Safari zwracają element BODY.
JW.
10
activeElementfaktycznie nie zwraca skupionego elementu. Dowolny element może być skoncentrowany. Jeśli dokument ma 4 „przewijane”, 0 lub 1 z tych div można przewijać za pomocą klawiszy strzałek. Jeśli klikniesz jeden, div zostanie skoncentrowany. Jeśli klikniesz poza wszystkim, ciało jest skupione. Jak dowiedzieć się, na czym skupia się scrolldiv? jsfiddle.net/rudiedirkx/bC5ke/show (sprawdź konsolę)
Rudie,
18
@Rudie, @Stewart: Zbudowałem twoje skrzypce, aby stworzyć bardziej skomplikowany plac zabaw: jsfiddle.net/mklement/72rTF . Przekonasz się, że jedyną dużą przeglądarką (pod koniec 2012 r.), Która może skupić się na takim, divjest Firefox 17 i tylko poprzez tabulator . Typy elementów, które zwracają WSZYSTKIE główne przeglądarki, document.activeElementsą ograniczone do elementów związanych z wprowadzaniem danych . Jeśli fokus nie ma takiego elementu, wszystkie główne przeglądarki zwracają bodyelement - z wyjątkiem IE 9, który zwraca htmlelement.
mklement0
10
Nie jestem pewien, czy to pomaga, ale możesz sprawić, że element taki jak div otrzyma fokus na klawiaturze, włączając atrybut tabindex = "0"
Marco Luglio
4
Każdy dostęp do document.activeElementpowinien być zawinięty, try catchponieważ w niektórych okolicznościach może generować wyjątek (nie tylko IE9 AFAIK). Zobacz bugs.jquery.com/ticket/13393 i bugs.jqueryui.com/ticket/8443
robocat
127

Jak powiedział JW, nie można znaleźć bieżącego elementu skupionego, przynajmniej w sposób niezależny od przeglądarki. Ale jeśli twoja aplikacja jest tylko IE (niektóre są ...), możesz ją znaleźć w następujący sposób:

document.activeElement

EDYCJA: Wygląda na to, że IE nie miał przecież wszystkiego źle, jest to część szkicu HTML5 i wydaje się być obsługiwane przez najnowszą wersję Chrome, Safari i Firefox.

Wookai
źródło
5
FF3 też. Jest to właściwie część specyfikacji HTML5 dotyczącej „zarządzania skupieniem”.
Crescent Fresh
2
Działa w aktualnej wersji Chrome i Opera (9.62). Nie działa w Safari 3.2.3 na OS X, ale działa w Safari 4, które zostało wydane wczoraj :)
Gregers,
wciąż to samo dla chrome 19: S
Sebas
1
Działa tylko w chrome (20) / safari (5.1.3), gdy używasz klawiatury do tabulacji na elemencie. Jeśli klikniesz na to, ani jquery: focus selector, ani document.activeElement nie zwrócą tego, co kliknąłeś (odpowiednio zwracając niezdefiniowany i element treści dokumentu). PS Nie mogę uwierzyć, że ten wątek ma 2 lata i nadal występują problemy z regresją w zestawie internetowym, wraz z tym, w którym łącza pomijane nie działają, ale tyle pracy włożono w dodanie eksperymentalnego css3. Pomyśl, że mogę wrócić do polecania firefox mojej rodzinie i przyjaciołom.
Świt
^^ document.activeElement jest w porządku, gdy klikniesz, aby skupić się na polu tekstowym lub innym wprowadzeniu. Jeśli jest to link, nie będzie się on skupiał po kliknięciu (na stronie lub oczywiście w inny sposób).
marksyzm 10.09.13
84

Jeśli możesz korzystać z jQuery, obsługuje teraz: focus, upewnij się, że używasz wersji 1.6+.

To oświadczenie zapewni ci aktualnie skoncentrowany element.

$(":focus")

Od: Jak wybrać element, który skupia się na nim za pomocą jQuery

William Denniss
źródło
4
To dobrze, ale jak robi to jQuery? document.activeElement? Znalazłem to: return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
Harry Pehkonen,
46

document.activeElementjest teraz częścią specyfikacji roboczej wersji HTML5 , ale może nie być jeszcze obsługiwany w niektórych przeglądarkach innych niż główne / mobilne / starsze. Możesz wrócić do querySelector(jeśli jest to obsługiwane). Warto również wspomnieć, że document.activeElementzwróci się, document.bodyjeśli żaden element nie jest skoncentrowany - nawet jeśli okno przeglądarki nie ma fokusa.

Poniższy kod obejmie ten problem i wróci do querySelectornieco lepszego wsparcia.

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

Dodatkową rzeczą, na którą należy zwrócić uwagę, jest różnica w wydajności między tymi dwiema metodami. Zapytanie o dokument za pomocą selektorów zawsze będzie znacznie wolniejsze niż uzyskiwanie dostępu doactiveElement właściwości. Zobacz ten test jsperf.com .

Andy E.
źródło
21

Sam w sobie document.activeElementmoże nadal zwracać element, jeśli dokument nie jest skoncentrowany (a zatem nic w dokumencie jest skoncentrowane!)

Państwo może chcieć to zachowanie, albo może nie materia (np w keydownprzypadku), ale jeśli chcesz wiedzieć coś jest rzeczywiście koncentruje można dodatkowo sprawdzićdocument.hasFocus() .

Poniższe informacje przedstawiają element skupiony, jeśli taki istnieje, lub inny element null.

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

Aby sprawdzić, czy specyficzny element ma ostrość, to prostsze:

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

Aby sprawdzić, czy coś jest skoncentrowane, jest to znowu bardziej złożone:

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

Uwaga na temat niezawodności : w kodzie, w którym sprawdza on, document.bodyoraz document.documentElementdlatego, że niektóre przeglądarki zwracają jedną z nich lub nullgdy nic nie jest skoncentrowane.

Nie bierze pod uwagę, czy <body>(a może <html>) miał tabIndexatrybut, a zatem mógł się skupić . Jeśli piszesz bibliotekę lub coś i chcesz, aby była solidna, prawdopodobnie powinieneś jakoś sobie z tym poradzić.


Oto ( jednokierunkowa) wersja ( ciężkie cytaty) uzyskiwania skupionego elementu, która jest koncepcyjnie bardziej skomplikowana, ponieważ musisz wiedzieć o zwarciach, a wiesz, oczywiście, że nie pasuje do jednej linii, zakładając, że chcę, żeby było czytelne.
Nie polecę tego. Ale jeśli masz 1337 hax0r, idk ... to tam jest.
Możesz również usunąć tę || nullczęść, jeśli falsew niektórych przypadkach nie masz nic przeciwko . (Możesz nadal uzyskać, nulljeśli document.activeElementjest null):

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

Do sprawdzania, czy element specyficzny koncentruje alternatywnie Państwo mogli użyć zdarzenia, ale w ten sposób wymaga konfiguracji (i potencjalnie porzuca), i co najważniejsze, zakłada stan początkowy :

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

Możesz naprawić założenie stanu początkowego, używając nie-zdarzeń, ale równie dobrze możesz po prostu użyć tego zamiast.

1j01
źródło
15

document.activeElementmoże przyjmować wartość domyślną dla <body>elementu, jeśli nie są aktywne żadne elementy, na które można ustawić ostrość. Dodatkowo, jeśli element jest skupiony, a okno przeglądarki jest rozmyte,activeElement nadal będzie utrzymywał skupiony element.

Jeśli któryś z tych dwóch zachowań nie są pożądane, należy rozważyć podejście oparte na CSS: document.querySelector( ':focus' ).

Nate Whittaker
źródło
Fajnie, tak, w moim przypadku twoje podejście miało absolutnie sens. Mogę ustawić moje elementy, na których można ustawiać ostrość, za pomocą „tabindex =" - 1 "', jeśli żaden z nich nie ma fokusa (powiedzmy, tekstu lub obrazu, o które mnie to nie obchodzi), zwraca document.querySelector (': focus ') zero.
Manfred
Zobacz moją odpowiedź, aby uniknąć używania querySelector: stackoverflow.com/a/40873560/2624876
1j01
10

Podobało mi się podejście zastosowane przez Joela S., ale lubię też prostotę document.activeElement. Użyłem jQuery i połączyłem oba. Starsze przeglądarki, które nie obsługują, document.activeElementbędą używać jQuery.data()do przechowywania wartości „hasFocus”. Będą używane nowsze przeglądarki document.activeElement. Zakładam, że document.activeElementbędzie miał lepszą wydajność.

(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
źródło
3
Czy można to zastąpić @William Denniss's $("*:focus")?
Pylinux
Chyba tak. Napisałem to dawno temu i nigdy nie miałem powodu, aby ponownie szukać lepszego rozwiązania, skoro jest to 5 lat później. Wypróbuj to! Mogę po prostu zrobić to samo. Mam mniej wtyczek na naszej stronie! :)
Jason
10

Mały pomocnik, którego użyłem do tych celów w 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);

W ten sposób możesz sprawdzić, czy element ma fokus el.hasFocus()pod warunkiem, że startFocusTracking()został wywołany na danym elemencie.

Joel S.
źródło
7

JQuery obsługuje obecnie :focuspseudoklasę. Jeśli szukasz tego w dokumentacji JQuery, sprawdź w „Selektorach”, gdzie prowadzi do dokumentów W3C CSS . Testowałem z Chrome, FF i IE 7+. Pamiętaj, że aby działał w IE, <!DOCTYPE...musi istnieć na stronie HTML. Oto przykład, zakładając, że przypisałeś identyfikator do elementu, który ma fokus:

$(":focus").each(function() {
  alert($(this).attr("id") + " has focus!");
});
DeezCashews
źródło
1
Powinieneś (zawsze?) Używać this.idzamiast $(this).attr('id')lub przynajmniej (kiedy już masz swój obiekt jQuery) $(this)[0].id. Natywny Javascript na tym poziomie jest ZNACZNIE szybszy i bardziej wydajny. W tym przypadku może nie być zauważalny, ale w całym systemie zauważysz różnicę.
Martijn
7

Jeśli chcesz uzyskać obiekt, który jest instancją Element, musisz użyć document.activeElement, ale jeśli chcesz uzyskać obiekt, który jest instancją Text, musisz użyć document.getSelection().focusNode.

Mam nadzieję, że pomoże.

rplaurindo
źródło
Lepiej w jaki sposób?
1j01,
Otwórz inspektora przeglądarki, kliknij dowolne miejsce strony, przejdź obok tego document.getSelection().focusNode.parentElementi naciśnij Enter. Po tym, przeszłość document.activeElementi zrób to sametpling. ;)
rplaurindo
Z tego pola komentarz skupiony, document.activeElementdaje <textarea>natomiast document.getSelection().focusNodedaje <td>, że zawiera <textarea>(i document.getSelection().focusNode.parentElementdaje <tr>zawierający <td>)
1j01
Przepraszam, przepraszam. Nie wyjaśniłem tego dobrze. Jeśli chcesz uzyskać obiekt, który jest instancją Element, musisz użyć document.activeElement, ale jeśli chcesz uzyskać obiekt, który jest instancją Text, musisz użyć document.getSelection().focusNode. Sprawdź to jeszcze raz. Mam nadzieję, że pomogłem.
rplaurindo
2
Pytanie dotyczy tego, który element aktualnie się koncentruje. I focusNodenie jest gwarancją węzłem tekstowym albo.
1j01
6

Istnieją potencjalne problemy z użyciem document.activeElement. Rozważać:

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

Jeśli użytkownik skupia się na wewnętrznej div, wtedy document.activeElement nadal odwołuje się do zewnętrznej div. Nie możesz użyć document.activeElement, aby określić, który z wewnętrznych div ma fokus.

Obejdzie to następująca funkcja i zwróci skoncentrowany węzeł:

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

Jeśli wolisz uzyskać element skupiony, użyj:

function active_element(){
  var anchor = window.getSelection().anchorNode;
  if(anchor.nodeType == 3){
        return anchor.parentNode;
  }else if(anchor.nodeType == 1){
        return anchor;
  }
}
Nathan K.
źródło
3
To nie jest tak naprawdę problem document.activeElement: wewnętrzne <div>elementy w rzeczywistości nie mogą otrzymać fokusu, co można zobaczyć wizualnie, ustawiając :focuspseudoklasę na coś widocznego (przykład: jsfiddle.net/4gasa1t2/1 ). Mówisz o tym, który z elementów wewnętrznych <div>zawiera zaznaczenie lub kursor, co jest osobną kwestią.
Tim Down
6

Uważam, że następujący fragment kodu jest przydatny podczas próby ustalenia, który element jest aktualnie aktywny. Skopiuj następujące elementy do konsoli przeglądarki, a co sekundę będą drukować szczegóły bieżącego elementu, który ma fokus.

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

Zmodyfikuj, console.logaby wylogować się z czegoś innego, co pomoże ci dokładnie określić element, jeśli wydrukowanie całego elementu nie pomoże w ustaleniu elementu.

vegemite4me
źródło
5

Czytanie innych odpowiedzi i samodzielne wypróbowanie document.activeElementdaje ci element, którego potrzebujesz w większości przeglądarek.

Jeśli masz przeglądarkę, która nie obsługuje document.activeElement, jeśli masz jQuery w pobliżu, powinieneś być w stanie wypełnić ją wszystkimi zdarzeniami fokusowymi czymś bardzo prostym, takim jak to (niesprawdzone, ponieważ nie mam przeglądarki spełniającej te kryteria ):

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
źródło
5

Jeśli używasz jQuery, możesz użyć tego, aby dowiedzieć się, czy element jest aktywny:

$("input#id").is(":active");
Arne
źródło
4

W dojo możesz użyć dijit.getFocus ()

Daniel Hartmann
źródło
3

Po prostu umieszczam to tutaj, aby dać rozwiązanie, które w końcu wymyśliłem.

Utworzyłem właściwość o nazwie document.activeInputArea i użyłem dodatku HotKeys jQuery'ego do przechwytywania zdarzeń klawiatury dla klawiszy strzałek, tab i enter, i utworzyłem moduł obsługi zdarzeń do klikania w elementy wejściowe.

Następnie dostosowywałem activeInputArea przy każdej zmianie fokusa, dzięki czemu mogłem użyć tej właściwości, aby dowiedzieć się, gdzie jestem.

Łatwo to jednak zepsuć, ponieważ jeśli masz błąd w systemie i fokus nie jest tam, gdzie myślisz, że jest, to bardzo trudno jest przywrócić prawidłowe fokus.

Tony Peterson
źródło