Kobieta czytająca list

Jak śledzić aktywny element na stronie? document.activeElement w praktyce

10 min. czytania

Ś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:

  1. Otwórz DevTools (F12 lub Ctrl+Shift+I) i przejdź do zakładki Console.
  2. Kliknij ikonę oka po lewej stronie pola filtrowania logów – to otwiera pole do tworzenia tzw. Live Expression.
  3. Wpisz w tym polu:

document.activeElement

  1. Zatwierdź Enter albo kliknij poza pole, np. na badaną stronę.
  2. Live Expression będzie teraz na bieżąco aktualizować swój wynik – mniej więcej co 250 ms – pokazując aktualny aktywny element.
  3. 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.activeElement w 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, albo shadowRoot,
  • 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.activeElement w 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:

  1. przed zmianą interfejsu zapisz document.activeElement,
  2. 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 tabindex lub 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.activeElement na 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.