Frontend Academy · Lekcja

Portale dla modali i podpowiedzi

Użyje Pan/Pani ReactDOM.createPortal do renderowania elementów potomnych w innym węźle DOM, co umożliwi tworzenie modali i podpowiedzi wychodzących poza kontenery overflow:hidden.

Lekcja 3 z 414 kroki

Portale dla modali i podpowiedzi to bezpłatna lekcja Frontend Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Frontend Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Frontend Academy zawiera 4 lekcji w sumie.

Problem z ucieczką z kontenera

Modale i tooltipy muszą być renderowane u góry DOM, poza ograniczeniami overflow:hidden, stosami z-index i kontekstami zakotwiczonymi przez transform ich rodzica. Rozwiązaniem są portale.

API createPortal

createPortal(children, container) renderuje children w elemencie container (dowolnym węźle DOM), zachowując je logicznie jako dzieci komponentu React na potrzeby obsługi zdarzeń i kontekstu.

import { createPortal } from 'react-dom';

function Modal({ children, onClose }) {
  return createPortal(
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal-content" onClick={e => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    document.body
  );
}

Kontenery portali

Typowe miejsca docelowe to: document.body, przeznaczony do tego element #modal-root w pliku index.html lub kontener wskazywany przez ref i tworzony na żądanie.

<!-- index.html -->
<div id="root"></div>
<div id="modal-root"></div>

// component:
const modalRoot = document.getElementById('modal-root');
return createPortal(<div>...</div>, modalRoot);

Dlaczego nie renderować bezpośrednio?

Jeśli rodzic ma overflow: hidden, modal potomny zostanie przycięty. Jeśli rodzic ma transform: scale, pozycjonowanie fixed modalu stanie się względne względem rodzica. Portale pozwalają uniknąć obu tych problemów.

Zdarzenia nadal propagują się w górę

Mimo renderowania w innym miejscu DOM zdarzenia React propagują się przez drzewo komponentów, a nie przez drzewo DOM. Kliknięcie w portalu nadal dociera do procedur obsługi onClick w nadrzędnych komponentach React.

Zamykanie po kliknięciu nakładki

Należy zatrzymać propagację zdarzeń w treści modalu, aby kliknięcia wewnątrz nie zamykały modalu.

function Modal({ onClose, children }) {
  return createPortal(
    <div className="backdrop" onClick={onClose}>
      <div className="content" onClick={e => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    document.body
  );
}

Zamykanie klawiszem Escape

Należy dodać nasłuchiwanie zdarzenia keydown, gdy modal jest otwarty, i usunąć je podczas odmontowywania.

useEffect(() => {
  const onKey = (e) => { if (e.key === 'Escape') onClose(); };
  window.addEventListener('keydown', onKey);
  return () => window.removeEventListener('keydown', onKey);
}, [onClose]);

Zarządzanie fokusem w portalach

Po otwarciu modalu należy przenieść fokus do jego wnętrza, uwięzić fokus do chwili zamknięcia, a następnie przywrócić fokus elementowi, który otworzył modal. Warto użyć biblioteki takiej jak focus-trap-react, aby nie implementować tego samodzielnie.

Natywny element dialog

Element <dialog> w HTML natywnie obsługuje pozycjonowanie modalu, tło, pułapkę fokusu i zamykanie klawiszem Escape. W przypadku większości modali jest lepszym punktem wyjścia niż portal.

function Modal({ open, onClose, children }) {
  const ref = useRef(null);
  useEffect(() => {
    if (open) ref.current?.showModal();
    else ref.current?.close();
  }, [open]);
  return (
    <dialog ref={ref} onClose={onClose}>
      {children}
    </dialog>
  );
}

Tooltipy i popovery

Tooltipy są pozycjonowane względem elementu kotwiczącego. Portal pozwala im wyjść poza konteksty stosu. Można połączyć go z floating-ui, aby obsługiwać wykrywanie kolizji, zmianę strony i pozycjonowanie.

import { useFloating, autoUpdate } from '@floating-ui/react';

function Tooltip({ children, content }) {
  const { refs, floatingStyles } = useFloating({ whileElementsMounted: autoUpdate });
  const [open, setOpen] = useState(false);
  return (
    <>
      <button ref={refs.setReference} onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)}>
        {children}
      </button>
      {open && createPortal(
        <div ref={refs.setFloating} style={floatingStyles}>{content}</div>,
        document.body
      )}
    </>
  );
}

Uwaga dotycząca SSR

document nie istnieje po stronie serwera. Należy albo renderować wyłącznie po stronie klienta (sprawdzając typeof window), albo użyć dynamic z ssr:false w Next.js.

Dostępność: aria-modal

Kontenery modali powinny mieć role="dialog", aria-modal="true" oraz aria-labelledby wskazujące nagłówek modalu. Można też użyć natywnego <dialog>.

Szybki test

Dlaczego w przypadku modali należy używać createPortal zamiast renderować je bezpośrednio w drzewie komponentów?

Podsumowanie: portale

createPortal(children, container) renderuje elementy DOM w innym miejscu, zachowując logiczne drzewo React. Rozwiązuje problemy z wydostawaniem się poza overflow, transform i z-index. Zdarzenia nadal propagują się przez drzewo React. Należy dodać zamykanie klawiszem Escape i zarządzanie fokusem. Natywny <dialog> obsługuje znaczną część tych funkcji w przypadku modali. Tooltipy można połączyć z floating-ui. Nie należy zapominać o SSR (sprawdzaniu typeof window).

Bezpłatny start

Ucz się HTML dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
41
Lekcje
163

Często zadawane pytania

Czy lekcja „Portale dla modali i podpowiedzi” jest bezpłatna?

Tak — pełny tekst „Portale dla modali i podpowiedzi” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Frontend Academy, przejdź na CoddyKit PRO. Kurs Frontend Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Portale dla modali i podpowiedzi”?

Użyje Pan/Pani ReactDOM.createPortal do renderowania elementów potomnych w innym węźle DOM, co umożliwi tworzenie modali i podpowiedzi wychodzących poza kontenery overflow:hidden. Ćwiczysz Frontend Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Frontend Academy?

Nie wymagamy żadnego doświadczenia. Frontend Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Portale dla modali i podpowiedzi”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Frontend Academy?

Tak. Każda lekcja Frontend Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Komponenty złożone z Context
  2. Wzorce Render Props i HOC
  3. Portale dla modali i podpowiedzi
  4. Granice błędów
← Powrót do Frontend Academy