Uwaga! Trwają prace nad nową wersją serwisu. Mogą występować przejściowe problemy z jego funkcjonowaniem. Przepraszam za niedogodności!

⛔ Potrzebujesz wsparcia? Oceny CV? A może Code Review? ✅ Dołącz do naszej społeczności na Discordzie!

Jak stworzyć trzypoziomowe drop-down menu w HTML i CSS

Pokazuj i ukrywaj elementy menu po najechaniu kursorem!

Jak stworzyć menu na stronę jedynie za pomocą HTML-a i CSS-a? Poziom artykułu zakłada, że znasz już podstawy obu tych technologii. W treści skupimy się na odpowiednim ustawieniu elementów menu względem siebie, ukrywaniu i odkrywaniu drop-downów zależnie od pozycji kursora oraz na zmianie tła elementów menu po najechaniu na nie myszą.

Chcesz lepiej zapamiętać to zagadnienie? Wykonaj warsztat z HTML i CSS: Podstawy, aby osiągnąć swój cel!

Spis treści

 Czym jest drop-down i menu trzypoziomowe

Zanim przejdziemy do rzeczy, upewnijmy się, że mówimy o tym samym. Na poniższej grafice widzisz menu trzypoziomowe z dwoma drop-downami:

Menu z dwoma otwartymi dropdownami. Na górze niebieski pasek menu z pozycjami: Strona główna, Oferta i Kontakt.

Drop-down to okienko pojawiające się po (zależnie od implementacji) kliknięciu lub najechaniu na element menu, a poziom to każda z „sekcji” menu. W naszym przypadku pierwszy poziom jest bezpośrednio widoczny dla użytkownika w postaci górnego paska. Drugi poziom pojawia się po najechaniu kursorem na pozycję „Oferta”, a trzeci poziom – po najechaniu na pozycję „Kursy wideo”.

Zrozumienie tego pomoże nam zaplanować strukturę HTML.

Jeszcze mała uwaga do urządzeń mobilnych: tam hover (najechanie na element) zostanie aktywowany po tapnięciu. Nie ma wskaźnika, który mógłby przesuwać się ponad elementem, jak na przykład na desktopie robi to kursor.

Kod przedstawionego w artykule rozwiązania znajdziesz na repozytorium na GitHubie. Możesz też zobaczyć jego działanie dzięki GitHub Pages.

 Struktura HTML menu trzypoziomowego z drop-downami

 Pierwszy poziom

Zacznijmy od stworzenia pierwszego poziomu menu:

<nav>
  <ul>
    <li><a href="#home">Strona główna</a></li>
    <li><a href="#offer">Oferta</a></li>  // tu docelowo będzie drop-down
    <li><a href="#contact">Kontakt</a></li>
  </ul>
</nav>

Później dodamy do tych elementów odpowiednie klasy, które pomogą nam w stylowaniu, lecz na razie chcę, aby struktura menu była dla Ciebie jak najbardziej przejrzysta.

Uwaga na zawartość listy nieuporządkowanej

Pierwszy poziom nie stanowi problemu. Element nav semantycznie pasuje do nawigacji, za którą przecież odpowiada menu, a lista nieuporządkowana ul ma w sobie jedynie dzieci, które zgodnie z dokumentacją może zawierać. Przykładowo na MDN w podpunkcie Permitted content przeczytamy: Zero or more <li>, <script> and <template> elements.

Co to oznacza? Oznacza to, że nie powinniśmy umieszczać wewnątrz ul np. linków w taki sposób:

<ul>
  <a href="#"><li>Strona główna</li></a>
  <a href="#offer"><li>Oferta</li></a>
  <a href="#contact"><li>Kontakt</li></a>
</ul>

Nieprawidłowa semantycznie struktura może osłabiać dostępność naszej strony dla osób korzystających np. z czytników ekranowych czy innych narzędzi analizujących kod HTML. Trzymajmy się więc tego, że linki umieszczamy dopiero wewnątrz elementów li.

 Drugi poziom

Zadajmy sobie tutaj dwa UX-owe pytania:

  1. Czy pozycja menu, pod którą umieścisz drop-down, ma być klikalna, tzn. czy ma przekierowywać do jakiejś podstrony?
  2. Jak chcesz uprzedzić użytkownika o tym, że to lista rozwijana?

Na pierwsze pytanie odpowiem sobie „nie” (usunę więc link). Oczywiście zależy to też np. od wymagań klienta, ale z reguły pod względem doświadczenia użytkownika lepiej jest nie umieszczać linku pod pozycją z drop-downem.

Dlaczego? Ponieważ może się zdarzyć taki scenariusz:

  • użytkownik kliknie „Oferta”,
  • wyświetli mu się drop-down, ale nie zdąży nic z niego wybrać, bo…
  • zadziała przekierowanie ustawione pod słowem „Oferta” i przeniesie go gdzie indziej.

Denerwujące 😉

Na drugie pytanie odpowiem sobie tak: za słowem „Oferta” umieszczę strzałkę w dół, by było wiadomo, że kryje się pod nim jakaś lista. Można to zrobić np. z poziomu CSS-a, stylując pseudoelement after. Teraz jednak nie na tym będziemy się skupiać, skrócę więc sobie drogę i użyję symbolu ▼.

Oto nasz drugi poziom:

<nav>
  <ul>
     <li><a href="#home">Strona główna</a></li>
    <li>Oferta ▼
      <ul>
        <li><a href="#shop">Sklep</a></li>
        <li><a href="#courses">Kursy wideo</a></li>
      </ul>
    </li>
    <li><a href="#contact">Kontakt</a></li>
  </ul>
</nav>

Uwaga na zagnieżdżanie menu niższego poziomu (drop-downu)

Zasady są dwie:

1. Nową listę tworzymy wewnątrz elementu li należącego do menu wyższego poziomu (zwróć uwagę, czy masz tag zamykający </li>!),

<li>Oferta ▼
  <ul>
    <li><a href="#shop">Sklep</a></li>
    <li><a href="#courses">Kursy wideo</a></li>
  </ul>
</li>

2. Nowy poziom menu powinien być listą, czyli np. elementem ul (to raczej najczęstsza opcja), ol czy menu – będzie to poprawne pod względem semantyki.

Rozwijana pozycja menu jako link

Pomyślisz: OK, a co, jeśli ja chcę mieć podlinkowaną „Ofertę”, lecz jednocześnie umieścić pod nią drop-down? Wówczas robisz jak poniżej.

<nav>
  <ul>
     <li><a href="#home">Strona główna</a></li>
    <li><a href="#offer">Oferta ▼</a>
      <ul>
        <li><a href="#shop">Sklep</a></li>
        <li><a href="#courses">Kursy wideo</a></li>
      </ul>
    </li>
    <li><a href="#contact">Kontakt</a></li>
  </ul>
</nav>

Pamiętaj, aby umieścić tag zamykający </a> zaraz za nazwą pozycji menu, bo jeśli zrobisz to dopiero na końcu przed tagiem zamykającym </li>, to możesz napotkać spore problemy podczas stylowania!

 Trzeci poziom

Trzeci poziom i każdy kolejny tworzymy podobnie: wstawiamy go do elementu li i nie zapominamy zamknąć tagu. Ja zdecydowałam się jeszcze na usunięcie linku spod pozycji „Kursy”.

<nav>
  <ul>
     <li><a href="#home">Strona główna</a></li>
    <li>Oferta ▼
      <ul>
        <li><a href="#shop">Sklep</a></li>
        <li>Kursy ▶
          <ul>
            <li><a href="/html-css-course">HTML i CSS</a></li>
            <li><a href="/js-course">JavaScript</a></li>
          </ul>
        </li>
      </ul>
    </li>
    <li><a href="#contact">Kontakt</a></li>
  </ul>
</nav>

 Nadanie klas zgodnie z metodologią BEM

Jeżeli chcesz zgłębić tworzenie klas zgodnie z metodologią BEM, to zapraszam Cię do artykułu „Metodologia BEM w CSS i Sass”. Tymczasem nasze menu z klasami prezentuje się teraz tak:

<nav class="menu">
  <ul class="menu__list">
    <li class="menu__item">
      <a href="#home" class="menu__link">Strona główna</a>
    </li>
    <li class="menu__item menu__item--parent">
      Oferta ▼
      <ul class="menu__sublist menu__sublist--bottom">
        <li class="menu__item">
          <a href="#shop" class="menu__link">Sklep</a>
        </li>
        <li class="menu__item menu__item--parent">
          Kursy wideo ▶
          <ul class="menu__sublist menu__sublist--right">
            <li class="menu__item">
              <a href="/html-css-course" class="menu__link">HTML i CSS</a>
            </li>
            <li class="menu__item">
              <a href="/js-course" class="menu__link">JavaScript</a>
            </li>
          </ul>
        </li>
      </ul>
    </li>
    <li class="menu__item">
      <a href="#contact" class="menu__link">Kontakt</a>
    </li>
  </ul>
</nav>

 Stylowanie menu – CSS

 Pierwszy poziom (menu) – podstawowe style

Nasze menu ma formę górnego paska na stronie, zatem wystarczy – oprócz nadania koloru czy stylowania fontu – użyć np. flexboxa, by łatwo ustawić elementy obok siebie.

/* Basic style reset */
* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

ul {
  list-style: none;
}
a {
  text-decoration: none;
  display: block;
}

/* Main menu */
.menu {
  background-color: #3d3aeb;
  color: #fff;
  font-family: 'Courier New', Courier, monospace;
  font-size: 16px;
  font-weight: bold;
  white-space: nowrap;
}

.menu__list {
  display: flex;
}
.menu__link, .menu__item--parent {
  padding: 15px 20px;
}

.menu__link {
  color: #fff;
}

.menu__item--parent {
  cursor: default;
  position: relative;
}

W powyższym kodzie podświetlono trzy rzeczy:

  1. Deklaracja white-space: nowrap sprawia, że tekst nie będzie przeskakiwać do drugiej linijki.
  2. Padding dodajemy do elementu listy, pod którą kryje się submenu, oraz do linku. Dlaczego tak? Ponieważ dzięki temu uzyskujemy:
    1. linkowane pozycje menu klikalne na całej zajmowanej przez nie powierzchni (a nie tylko na ich tekście),
    2. ładny hover (o tym na końcu) – zmiana koloru po najechaniu myszką obejmuje całą powierzchnię pozycji menu, a nie tylko prostokąt wokół tekstu.
  3. Gdyby nie cursor: default dla elementu listy z submenu, to zamiast strzałki (pointer zarezerwowany jest dla elementów klikalnych) mielibyśmy pionowy kursor dla tekstu. Możesz usunąć tę deklarację, by sprawdzić jej działanie.

 Drugi i trzeci poziom (submenu) – odpowiednie ustawienie drop-downów

Teraz przechodzimy do prawidłowego ustawienia drop-downów. Docelowo będą one ukryte (stąd obecna już, ale zakomentowana deklaracja display: none). Do prawidłowego ustawienia list submenu użyjemy pozycjonowania absolutnego.

Rodzic/przodek, względem którego chcemy pozycjonować inny element, sam musi być pozycjonowany (mieć wartość inną niż domyślna static), dlatego element li, pod którym kryje się drop-down ma przypisaną deklarację position: relative (nie wpływa to na jego pozycję, jak np. wartość fixed).

.menu__item--parent {
  cursor: default;
  position: relative;
}

/* Drop-down */
.menu__sublist {
  display: none;
  position: absolute;
  background-color: #3d3aeb;
}

.menu__sublist--bottom {
  left: 0;
  top: 100%;
}
.menu__sublist--right {
  left: 100%;
  top: 0;
}

Teraz sublisty mogą otrzymać wartość absolute, co wyrzuci je poza ich dotychczasową pozycję i ustawi względem przodka pozycjonowanego relatywnie.

Aby drop-downy „wskoczyły na miejsce”, trzeba ustawić odpowiednie wartości dla top i left. Wartości te, jeżeli używamy procentów, korzystają z rozmiarów przodka pozycjonowanego relatywnie (dla ułatwienia nazwę go „przodkiem”).

Jeśli zatem drop-down drugiego poziomu ma być bezpośrednio pod elementem „Oferta”, musi być odepchnięty od góry o 100% wysokości przodka. Jeśli drop-down trzeciego poziomu ma wyskakiwać bezpośrednio po prawej od „Kursy wideo”, musi być odepchnięty od lewej krawędzi o 100% szerokości przodka. Wartość 0 będzie oznaczała zrównanie się z daną krawędzią przodka.

 Zmiana koloru tła na hover

Gdy elementy są już prawidłowo ustawione, dodajmy zmianę koloru tła pozycji menu po najechaniu na nie kursorem:

/* Change background color on hover for menu and drop-down items */
.menu__item:hover {
  background-color: #201dca;
}

 Pokazywanie i ukrywanie drop-downów

Czas ukryć nasze drop-downy. Odkomentujmy więc deklarację display: none dla klasy .menu__sublist. Drop-downy zniknęły! Teraz sprawmy, by pokazywały się po najechaniu na odpowiedni element listy.

/* Show drop-down on hover of the parent menu item */
.menu__item--parent:hover > .menu__sublist {
  display: block;
}

Powyższy selektory czytamy tak: po najechaniu na element listy (li) – pod którym kryje się drop-down – dla bezpośredniego dziecka tego elementu, czyli sublisty (ul), ustaw display: block. Spowoduje to pokazanie się drop-downu użytkownikowi.

Jeżeli znasz tego zapisu ze znakiem większości, zapoznasz się z nim na przykład na MDN: Child combinator.

 

Wiesz już, czym jest menu trzypoziomowe i jaki jest mechanizm pokazywania drop-downów. Spróbuj teraz stworzyć własne menu. Inspirację znajdziesz na przykład na stronach takich jak Dribbble (dla UI/UX designerów i grafików). Kolejnym Twoim krokiem niech będzie sprawienie, by menu dobrze prezentowało się urządzeniach o różnych wielkościach ekranu.

Udostępnij ten artykuł:

Mentoring to efektywna nauka pod okiem doświadczonej osoby, która:

  • przekazuje Ci swoją wiedzę i nadzoruje Twoje postępy w zdobywaniu umiejętności,
  • uczy Cię dobrych praktyk i wyłapuje złe nawyki,
  • wspiera Twój rozwój i zwiększa zaangażowanie w naukę.

Mam coś dla Ciebie!

W każdy piątek rozsyłam motywujący do nauki programowania newsletter!

Dodatkowo od razu otrzymasz ode mnie e-book o wartości 39 zł. To ponad 40 stron konkretów o nauce programowania i pracy w IT.

PS Zazwyczaj wysyłam 1-2 wiadomości na tydzień. Nikomu nie będę udostępniał Twojego adresu e-mail.