Dwuczęściowe wycentrowane menu w CSS
07 lipca 2006
Trzynaście miesięcy temu walczyłem z dość zaawansowanym menu DHTML z gry MMORPG Dark Throne próbując zamienić je na wersję CSS-ową. Poległem wtedy, nie uzyskując zamierzonego efektu.
Pomimo twierdzeń niektórych osób, że HTML-a i CSS-a można nauczyć się w tydzień, mi zajęło to trochę dłużej. Lecz poświęcony czas zwrócił się z nawiązką - nowe menu w CSS działa dokładnie tak jak sobie wymarzyłem. Działa pod Firefoksem, Operą oraz (ha!) Explorerem 6 (+ powinien pod innymi nowoczesnymi przeglądarkami). :)
Krok 1 - znaczniki HTML
Na początku napisałem strukturę menu korzystając z zagnieżdzonych list nieuporządkowanych. Nie użyłem ani jednego obrazka, natomiast dodałem odpowiednie indentyfikatory do pozycji listy oraz linków.
Wprawne oko dopatrzy się wstawionych klas dla pierwszej listy nieuporządkowanej oraz umieszczonych w niej pozycji. Pomimo możliwości skorzystania z kaskady, użycie selektora klasy zaoszczędza kasowania reguł CSS dziedziczonych w kolejnych zagnieżdzeniach.
Owinąłem także tekst pozycji menu w dodatkowe znaczniki, pozwalające ukryć tekst i podmienić go obrazkiem.
Użyłem tagu prezentacyjnego <big/>, ponieważ ładnie podkreśla pierwsze pozycje menu przy wyłączonych stylach. Jeśli nie zgadzasz się z moim wyborem, użyj <span/>.
Wszystko otoczyłem jednym divem, który okaże się bardzo pomocny przy wyświetlaniem podmenu i czarowaniu z centrowaniem.
Krok 2 - prezentacja menu głównego
Tego kroku nie rozbijałem na wiele mniejszych, bo ogólnie rzecz biorąc wszystko jest proste.
Usunąłem zbędne marginesy i dopełnienia dla body, ul i li by nie przeszkadzały później. Wyłączyłem też wyświetlanie menu drugiego rzędu, później się nim zajmiemy.
Nadałem poziome tło kontenerowi div, wyznaczające wysokość pierwszego poziomu menu. Jako że całe menu operuje na obrazkach, ustawiłem wysokość kontenera na 42px (2 razy 21px) i dodałem ucinanie elementów wystających, które pomoże przy znikaniu tła już za chwilę.
#navigation {background: url(menu-top.gif) 0 0 repeat-x;height: 42px;overflow: hidden;}
Pozycje menu zamieniłem na elementy blokowe, nadałem każdemu określoną szerokość i ustawiłem obrazki teł korzystając z identyfikatorów. Tekstu nachodzącego na obrazki pozbyłem się przesuwając go z lewo. Dodałem także obrazki teł rozdzielające pozycje menu.
li.tab {display: block;float: left;background-repeat: no-repeat;background-position: 0 0;height: 21px;}#tab-main { background-image: url(tab-main.gif); width: 57px; }#tab-battle { background-image: url(tab-battle.gif); width: 73px; }...li.tab big {display: block;text-indent: -1000px;background: url(tiny-right.gif) 100% 0 no-repeat;padding-right: 1px;}
Znając faktyczną szerokość każdego elementu menu (width + padding) mogłem wycentrować w poziomie nadrzędny ul.
Wreszcie ustawiłem rollovery dla pozycji menu, korzystając z polecanego zlepiania obrazków.
#tab-main:hover { background-position: 0 -20px; }#tab-battle:hover { background-position: 0 -20px; }
Krok 3 - prezentacja menu podrzędnego
Zanim zacząłem bawić się stanem :hover wybrałem identyfikator pierwszego menu podrzędnego (li#tab-main) i użyłem go do przywrócenia wyświetlania. W ten sposób nie musiałem latać z myszką za każdym razem odświeżając stronę. Nie mówiąc już o IE, ale o tym później.
Drugorzędne menu też jest wycentrowane. Można by zacząć ustawiać szerokości i wyrównać marginesami do środka, jak zrobiłem to wyżej - ale uznałem to za bardzo nieeleganckie rozwiązanie. Jakiekolwiek dodanie / odjęcie elementu pozbawiałoby idealnego wyśrodkowania innych pozycji. Pikanterii dodaje konieczność użycia obrazków. Czyli jednak blokowe, prawda?
Otóż niekoniecznie. Użyłem tam elementów liniowych = tekstu. Tekst da się banalnie wręcz centrować, a przy zachowaniu odpowiednich warunków pozwala też zostać podmieniony obrazkiem. Wrócimy jeszcze do tego.
Teraz należy odpowiednio ułożyć drugorzędny pasek menu pod pierwszym. Skorzystałem z pozycjonowania względem głównego kontenera div. Ustawiając szerokość na 100% i odciągając menu do lewej stworzyłem długi na całą szerokość strony pasek, na którym można wyłożyć liniowo pozycje menu drugiego rzędu. I wyśrodkować je. :-)
#navigation {position: relative;}li#tab-main ul {display: block;position: absolute;left: 0;top: 21px;width: 100%;text-align: center;height: 21px;background: url(menu-bot.gif) 0 0 repeat-x;}
Internet Explorer tutaj mocno zamarudził, nie zgadzając się na rozciągnięcie bloku na szerokość strony. Trzeba więc było go zmusić:
li#tab-main ul {_width: expression(document.documentElement.clientWidth);}
W tym konkretnym przypadku potrzebowałem szerokości viewportu, więc użyłem specyficznego dla IE document.documentElement.clientWidth - ale być może w twoich projektach będzie inaczej - skorzystaj wtedy z pobrania offsetWidth kontenera dla menu o danym identyfikatorze - getElementById. Tak czy inaczej hack powoduje poprawne wyświetlanie się strony - ku uciesze mas.
Pozostało mi na koniec tylko podmienić tekst obrazkami. Jak już pisałem odpadło display:block-owanie, ale nic nie szkodzi. Tekstu pozbyłem się ustawiając visibility: hidden dla tego dodatkowego spana - dzięki czemu miejsce zajmowane przez litery nie zmniejszyło się. Wystarczyło później dograć wysokość linii i dopełnienie dla tekstu, margines dla elementów listy oraz ustawić obrazki teł (identyfikatory poszły w ruch).
li#tab-main li {display: inline;padding: 0 10px;}li#tab-main li a {text-decoration: none;line-height: 20px;background-repeat: no-repeat;background-position: 50% 0;padding: 0 5px;}li#tab-main li a span {visibility: hidden;}#main-members { background-image: url(img/members.gif); }#main-news { background-image: url(img/news.gif); }...
Rozsądne pomniejszanie / powiększanie tekstu (czyli nie „a co się stanie na 1800%”) nie powoduje problemów w odbiorze takiego menu.
Krok 4 - przełączanie menu myszką
Po dodaniu wszystkich obrazków do CSS-a możemy podmienić selektor li#tab-main na li.tab, pamiętając o wyłączeniu wyświetlania menu podrzędnych przez display: none.
Czas zająć się materializacją menu po wskazaniu myszą. Załatwia to prosta reguła:
li.tab:hover ul {display: block;}
Już widzę te zabiedzone miny i wołanie „aaale to nie działa na iiieee ;(”. Nie rozpaczajcie, istnieje prozaiczna (w zastosowaniu) metoda walki z brakiem pełnego wsparcia IE6 dla :hover. Wystarczy, że dodamy do katalogu ze stroną plik zachowania csshover.htc i podlinkujemy go w arkuszu dla Explorera 6:
body {behavior: url(csshover.htc);}
Plik csshover.htc zawiera funkcje JavaScript rozszerzające zachowanie Miśka - wszystkie :hover w CSS zaczynają działać. Zanim zaczniesz tupać za użyte przeze mnie expression i .htc, znajdź proszę zwykłego użytkownika Explorera surfującego z wyłączonym JavaScriptem.
No, miło że się rozumiemy. ;)))
I to by było na tyle - działa i wygląda świetnie fajnie. :) Komentarze mile widziane. Menu oprócz tego, że jest semantyczne i dostępne, pozostawia otwartą furtkę dalszym modyfikacjom, które (mam nadzieję) zaprezentuję niedługo w kolejnym poście. Tylko zamiast CSS pobawimy się JavaScriptem. :)


