Śledzenie aktywnego elementu na stronie jest kluczowe zarówno dla debugowania interfejsu, jak i dla zapewnienia dobrej dostępności – a centralnym narzędziem w JavaScript jest tutaj właściwość document.activeElement.
Poniżej znajdziesz rozbudowany, praktyczny przewodnik: od podstaw fokusowania, przez użycie document.activeElement w DevTools, po bardziej zaawansowane scenariusze (formularze, modale, Shadow DOM) i perspektywę dostępności.
Co to jest „aktywny element” na stronie?
W przeglądarce w danym momencie dokładnie jeden element może być „na celowniku” klawiatury – to element, który otrzymuje zdarzenia klawiaturowe (keydown, keyup, input itd.).
Ten element nazywa się elementem z fokusem (focused element) albo – w terminologii DOM – aktywnym elementem (active element).
Najczęstsze przypadki wyglądają tak:
- piszesz coś w polu tekstowym
<input>lub<textarea>– to ono ma fokus, - naciskasz
TAB– fokus przeskakuje między linkami, przyciskami, polami formularza, - klikasz myszą w przycisk – ten przycisk zwykle na moment staje się elementem aktywnym.
W JavaScript bieżący element z fokusem odczytasz w każdej chwili przez document.activeElement.
Właściwość document.activeElement – definicja i podstawy
Co zwraca document.activeElement?
Właściwość document.activeElement:
- zwraca aktualnie aktywny (sfokusowany) element w dokumencie – czyli element, który otrzyma zdarzenia klawiatury, jeśli użytkownik zacznie pisać,
- jest właściwością tylko do odczytu – nie można jej bezpośrednio przypisać nowej wartości (fokus ustawiasz metodą
.focus()na konkretnym elemencie), - zwraca obiekt typu
Element(np.HTMLInputElement,HTMLButtonElement,HTMLBodyElement).
Gdy żaden element nie ma fokusu, przeglądarki najczęściej traktują jako aktywny element <body>; w niektórych stanach (np. podczas ładowania lub gdy dokument nie jest aktywny) możesz otrzymać również null.
Przykład odczytu nazwy tagu aktywnego elementu:
const tagName = document.activeElement.tagName;
console.log(tagName); // np. 'INPUT', 'BUTTON', 'BODY'
Kiedy activeElement jest szczególnie przydatne?
Najczęstsze zastosowania obejmują:
- debugowanie problemów z fokusem (np. „fokus znika po zamknięciu modala”),
- tworzenie dostępnej nawigacji klawiaturą (np. skróty klawiszowe działające różnie w zależności od aktywnego elementu),
- testowanie poprawnej kolejności fokusu (
TAB-order), - budowę focus trapów w modalach, dialogach, komponentach typu „dropdown”.
Podstawowe przykłady użycia w JavaScript
Wyświetlanie aktualnie aktywnego elementu
Najprostszy debug wygląda następująco:
setInterval(() => {
const el = document.activeElement;
console.log('Aktywny element:', el ? el.tagName : 'BRAK');
}, 1000);
Ten kod co sekundę wypisuje tag bieżącego aktywnego elementu. Jeśli użytkownik naciska TAB i przeskakuje po stronie, w konsoli widzisz na bieżąco, który element przejmuje fokus.
Czy konkretny element ma teraz fokus?
Często chcesz tylko odpowiedzieć na pytanie: „Czy to konkretne pole / przycisk jest teraz aktywne?” Wtedy porównujesz document.activeElement z danym elementem:
const myInput = document.getElementById('email');
function isMyInputFocused() {
return document.activeElement === myInput; // true / false
}
To podejście podpowiada też prosty sposób sprawdzenia, czy jakikolwiek element ma fokus: porównanie z document.body (uwzględnij też możliwość null w rzadkich przypadkach):
const nothingFocused = document.activeElement === document.body;
Ustawianie fokusu
Choć document.activeElement jest tylko do odczytu, fokus ustawiasz na elementach metodą .focus():
const search = document.getElementById('search');
search.focus(); // teraz search jest document.activeElement
Następnie możesz użyć document.activeElement, aby potwierdzić, że fokus został nadany właściwemu elementowi.
Śledzenie aktywnego elementu w Chrome DevTools (Live Expression)
Do debugowania problemów z fokusem nie zawsze chcesz pisać kod na stronie. W Chrome DevTools masz bardzo wygodne narzędzie: Live Expression z document.activeElement.
Krok po kroku: Live Expression z document.activeElement
Wykonaj poniższe kroki:
- Otwórz DevTools (
F12lubCtrl+Shift+I) i przejdź do zakładki Console. - Kliknij ikonę oka po lewej stronie pola filtrowania logów – to otwiera pole do tworzenia tzw. Live Expression.
- Wpisz w tym polu:
document.activeElement
- Zatwierdź
Enteralbo kliknij poza pole, np. na badaną stronę. - Live Expression będzie teraz na bieżąco aktualizować swój wynik – mniej więcej co 250 ms – pokazując aktualny aktywny element.
- Używając klawisza
TAB, przechodź między elementami strony – wynik Live Expression będzie się automatycznie zmieniał.
Jak z tego korzystać w praktyce?
Poniżej znajdziesz trzy szybkie triki, które przyspieszają diagnozę:
- podświetlenie aktywnego elementu – najedź kursorem na wynik wyrażenia
document.activeElementw konsoli, a DevTools podświetli ten element na stronie; - skok do zakładki Elements – kliknij prawym przyciskiem wynik i wybierz „Reveal in Elements panel”, aby przejść do drzewa DOM z zaznaczonym elementem;
- zapisanie elementu do zmiennej globalnej – z menu kontekstowego wybierz „Store outerHTML as global variable”, aby zapisać element do zmiennej (np.
temp1), a potem badać jego atrybuty i style.
To niezwykle wygodny sposób na wizualne i interaktywne debugowanie fokusu – bez modyfikowania kodu aplikacji.
Właściwość document.activeElement w realnych scenariuszach
Formularze i walidacja
Podczas walidacji warto ustawić fokus na pierwszym błędnym polu i zarazem sprawdzić, czy użytkownik nie „zgubił się” gdzieś indziej.
form.addEventListener('submit', (event) => {
event.preventDefault();
const firstInvalid = form.querySelector('[aria-invalid="true"]');
if (firstInvalid) {
firstInvalid.focus();
console.log('Aktywny element po walidacji:', document.activeElement);
}
});
Takie zachowanie pomaga osobom korzystającym z klawiatury i czytników ekranu, bo fokus zawsze ląduje tam, gdzie problem jest najważniejszy.
Skróty klawiszowe zależne od kontekstu
Załóżmy, że Ctrl+S ma zapisywać formularz tylko wtedy, gdy fokus jest w tym konkretnym formularzu.
document.addEventListener('keydown', (event) => {
if (event.ctrlKey && event.key === 's') {
const active = document.activeElement;
if (form.contains(active)) {
event.preventDefault();
saveForm();
}
}
});
Dzięki document.activeElement możesz budować skróty kontekstowe, zamiast globalnych, które przeszkadzają użytkownikom.
Modale, dialogi i „focus trap”
Dobrze zaprojektowany modal przejmuje fokus od reszty strony, a po zamknięciu przywraca go tam, gdzie użytkownik był wcześniej. Poniższy schemat pokazuje najprostszy wzorzec:
let previousActiveElement;
function openModal() {
previousActiveElement = document.activeElement; // zapamiętaj
showModal(); // pokaż modal na ekranie
modal.focus(); // lub pierwszy fokusowalny element w modalu
}
function closeModal() {
hideModal();
if (previousActiveElement && previousActiveElement.focus) {
previousActiveElement.focus();
}
}
Taki wzorzec jest kluczowy dla dostępności, bo po zamknięciu dialogu użytkownik nie traci kontekstu.
Właściwość document.hasFocus() – czy dokument w ogóle ma fokus?
Czasem potrzebujesz wiedzieć, czy cały dokument jest aktywny (np. czy karta przeglądarki nie jest w tle). Do tego służy metoda document.hasFocus().
Zwraca true, gdy dokument lub dowolny element w nim zawarty ma fokus, oraz false, gdy użytkownik przełączył się do innej karty lub aplikacji.
Przykład użycia:
if (document.hasFocus()) {
console.log('Użytkownik jest w tej karcie.');
} else {
console.log('Karta w tle – nie wysyłamy powiadomienia.');
}
W połączeniu z document.activeElement możesz tworzyć logikę powiadomień lub autozapisu, która nie przeszkadza użytkownikowi w innych aplikacjach.
Shadow DOM a document.activeElement – ważna pułapka
W świecie Web Components wiele elementów tworzy własny Shadow DOM. document.activeElement widzi fokus tylko na poziomie dokumentu, nie zagląda do wnętrza Shadow DOM.
Typowy scenariusz wygląda tak:
- masz komponent
<my-input>z Shadow DOM, - wewnątrz niego jest
<input>, - użytkownik pisze w tym
<input>.
W document.activeElement zobaczysz najpewniej sam host, czyli <my-input>, a nie wewnętrzne <input>, bo to inne drzewo (shadow root).
Właściwość shadowRoot.activeElement – fokus wewnątrz Shadow DOM
Aby sprawdzić, co ma fokus wewnątrz konkretnego Shadow DOM-u, skorzystaj z poniższego podejścia:
const root = myComponent.shadowRoot;
const innerActive = root.activeElement; // element z fokusem w tym subtree
Właściwość shadowRoot.activeElement działa analogicznie do document.activeElement, ale w obrębie danego drzewa.
„Głęboko aktywny element” – pomocnik z getRootNode()
W nowoczesnych przeglądarkach możesz podejść odwrotnie:
- mając element (np. host komponentu), wywołaj na nim
getRootNode(), - otrzymasz albo
document, alboshadowRoot, - na tym obiekcie możesz sprawdzić
activeElement.
Przykładowa funkcja, która zwróci najgłębiej sfokusowany element:
function getDeepActiveElement(node = document) {
let active = node.activeElement;
while (active && active.shadowRoot) {
active = active.shadowRoot.activeElement;
}
return active;
}
console.log(getDeepActiveElement()); // najgłębiej sfokusowany element w całym drzewie
To podejście jest nieocenione w aplikacjach intensywnie korzystających z Web Components, gdy musisz dokładnie wiedzieć, gdzie znajduje się fokus.
Pseudoklasa :focus vs document.activeElement – CSS i JS razem
Po stronie CSS fokusem zarządzasz przez pseudoklasy :focus oraz :focus-visible. W JavaScript odpowiednikiem jest document.activeElement, które wskazuje ten sam element (w ramach jego drzewa DOM).
Dzięki temu łatwo powiążesz logikę JS z wyglądem w CSS. Przykładowy styl i sprawdzenie w JS:
button:focus {
outline: 3px solid #005fcc;
}
const active = document.activeElement;
if (active && active.matches('button')) {
console.log('Aktywny jest jakiś przycisk');
}
Nie usuwaj stylów fokusu bez zapewnienia wyraźnej alternatywy – użytkownicy klawiatury muszą widzieć, gdzie znajduje się kursor.
Perspektywa dostępności (a11y): jak document.activeElement pomaga?
Chociaż specyfikacje a11y nie mówią wprost o document.activeElement, praktyka pokazuje, że to narzędzie bardzo pomaga w budowaniu i testowaniu dostępnych interfejsów.
Testowanie kolejności fokusu
Podczas audytu dostępności możesz wykonać następujące kroki:
- włączyć Live Expression z
document.activeElementw DevTools, - przechodzić po stronie klawiszem
TAB, - obserwować, czy fokus przeskakuje w logicznej kolejności (np. od nagłówka do zawartości, potem do panelu bocznego, itd.).
Jeśli zauważysz:
- nagłe skoki fokusu w nieoczekiwane miejsca,
- „uwięzienie” fokusu (brak możliwości wyjścia z elementu),
- powrót fokusu do początku strony,
to sygnał, że trzeba poprawić semantykę, kolejność elementów, tabindex lub logikę JS.
Obsługa błędów i komunikatów
Dobra praktyka a11y to przeniesienie fokusu na komunikat o błędzie lub pierwsze błędne pole i jednoczesne upewnienie się (np. przez document.activeElement), że nie zostawiasz użytkownika „w próżni”.
Przykładowy test automatyczny (np. w Playwright / Cypress):
// po submit z błędami
await expect(page.evaluate(() => document.activeElement.id))
.resolves.toBe('firstErrorField');
Powrót do miejsca, w którym użytkownik był
Aby poprawić tzw. orientację przestrzenną użytkownika w interfejsie, postępuj następująco:
- przed zmianą interfejsu zapisz
document.activeElement, - po zakończeniu operacji przywróć fokus do tego elementu (o ile nadal istnieje).
Najczęstsze problemy i dobre praktyki
Właściwość document.activeElement zwraca zawsze <body>
Jeśli w aplikacji ciągle widzisz BODY jako aktywny element, potencjalne przyczyny są następujące:
- strona nie nadaje fokusu żadnemu interaktywnemu elementowi po załadowaniu,
- elementy są niefokusowalne (brak
tabindexlub nie są to natywne elementy interaktywne), - JS nadpisuje fokus w nieoczekiwanych momentach (np. automatyczne
.blur()).
Praktyczne rozwiązania wyglądają tak:
- upewnij się, że kluczowe elementy (linki, przyciski, pola) są semantycznymi elementami HTML,
- do niestandardowych komponentów używaj
tabindex="0"i odpowiedniej obsługi klawiatury, - kontroluj miejsca, w których wywołujesz
.focus()i.blur().
Brak reakcji na skróty klawiszowe
Jeśli skróty klawiszowe działają „czasem”, sprawdź poniższe punkty:
- czy dokument ma fokus (
document.hasFocus()), - jaki element jest aktywny (
document.activeElement), - czy ten element nie „przechwytuje” skrótów (np. pole tekstowe, w którym wpisywany jest skrót).
Możesz np. ignorować skróty, gdy aktywny element jest polem tekstowym:
document.addEventListener('keydown', (event) => {
const active = document.activeElement;
if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA')) {
return; // nie przeszkadzaj użytkownikowi w pisaniu
}
// globalne skróty
});
Problemy z komponentami w Shadow DOM
Jeśli document.activeElement mówi jedno, a wizualnie widzisz co innego, najczęściej dzieje się tak:
- używasz komponentów z Shadow DOM,
- sprawdź
shadowRoot.activeElementna hostach komponentów, - rozważ użycie helpera typu
getDeepActiveElement()(pokazanego wcześniej).
Świadome zarządzanie fokusem skraca debugowanie, poprawia UX i czyni interfejs realnie dostępnym dla wszystkich.






