Perfection or Vanity

Project: Terminated

Blog nie jest już dalej prowadzony ani aktualizowany. Mimo tego, wpisy i komentarze są dalej dostępne. Możesz przeczytać pożegnalny wpis albo przejść do archiwum.

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ę.

  1. #navigation {
  2. background: url(menu-top.gif) 0 0 repeat-x;
  3. height: 42px;
  4. overflow: hidden;
  5. }

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.

  1. li.tab {
  2. display: block;
  3. float: left;
  4. background-repeat: no-repeat;
  5. background-position: 0 0;
  6. height: 21px;
  7. }
  8. #tab-main { background-image: url(tab-main.gif); width: 57px; }
  9. #tab-battle { background-image: url(tab-battle.gif); width: 73px; }
  10. ...
  11. li.tab big {
  12. display: block;
  13. text-indent: -1000px;
  14. background: url(tiny-right.gif) 100% 0 no-repeat;
  15. padding-right: 1px;
  16. }

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.

  1. #tab-main:hover { background-position: 0 -20px; }
  2. #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. :-)

  1. #navigation {
  2. position: relative;
  3. }
  4. li#tab-main ul {
  5. display: block;
  6. position: absolute;
  7. left: 0;
  8. top: 21px;
  9. width: 100%;
  10. text-align: center;
  11. height: 21px;
  12. background: url(menu-bot.gif) 0 0 repeat-x;
  13. }

Internet Explorer tutaj mocno zamarudził, nie zgadzając się na rozciągnięcie bloku na szerokość strony. Trzeba więc było go zmusić:

  1. li#tab-main ul {
  2. _width: expression(document.documentElement.clientWidth);
  3. }

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).

  1. li#tab-main li {
  2. display: inline;
  3. padding: 0 10px;
  4. }
  5. li#tab-main li a {
  6. text-decoration: none;
  7. line-height: 20px;
  8. background-repeat: no-repeat;
  9. background-position: 50% 0;
  10. padding: 0 5px;
  11. }
  12. li#tab-main li a span {
  13. visibility: hidden;
  14. }
  15. #main-members { background-image: url(img/members.gif); }
  16. #main-news { background-image: url(img/news.gif); }
  17. ...

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:

  1. li.tab:hover ul {
  2. display: block;
  3. }

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:

  1. body {
  2. behavior: url(csshover.htc);
  3. }

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. :)

Informacje i hiperłącza

Blog o projektowaniu zgodnych ze standardami stron internetowych.

Praktyczne przykłady, sztuczki CSS, sposoby obchodzenia błędów przeglądarek, lekki i nieinwazyjny JavaScript, użyteczny design, dostępność i skrypty użytkownika.

RSS

Informacje o wpisie

Napisał riddle 07 lipca 2006 o 02:49

Kategorie: CSS, Internet Explorer, Webdesign na luzie

Dodaj do:

Wpisy archiwalne

Archiwum miesięczne

Dzięki!

Dodaj bloga do Technorati Favorites Dodaj bloga do Del.icio.us Blog należy do sieci 10przykazań.com

  1. Wszystko fajnie, tylko co z dostępnością takiego hover menu?
    Mnie osobiście szlag by trafiał, gdyby w jakiejś desktopowej aplikacji menu rozwijało się od razu po najechaniu na nie myszą, zamiast po kliknięciu.

    Dla osób z upośledzeniem ruchu takie menu też są udręką, bo wymagają precyzyjnego prowadzenia myszy - najeżdżasz na pozycję w głównym menu, a potem precyzyjnie musisz wjechać na pozycję w menu podrzędnym tak, żeby nie zjechać poza obszar menu, co od razu spowoduje jego zwinięcie.

    Myślę, że takie menu (ja ostatnio coś takiego zrobiłem tu: nowe.karko.net) powinno działać na click, a nie hover. Co oczywiście wyklucza użycie tylko CSSa.

    Pozdrawiam

  2. Niestety zjechanie kursorem myszy na bok powoduje zniknięcie menu podrzędnego.

  3. U mnie wygląda to tak:

    http://mroq.php5.pl/dark-throne.png

  4. A mnie się to nawet podoba. Noi poza tym wygląda jednakowo w "popularnych" przeglądarkach.

  5. Jeśli chodzi o Operę działa tylko w wersji >=9.0

  6. Jak Opera to tylko 9.0, sorry… to nie warunki rządowo-terminalowe z Win 95 i IE 4.0 na pokładzie. Polecam zrobić upgrade.

    Bellois: gdzie?
    gshegosh: Ostatni akapit. Proszę!

  7. szkoda tylko ze po najechaniu trzeba czekac az zaladuja sie obrazki.. na wolniejszych laczach ktos moze sie nie domyslic nawet ze tam cos jest (jakies menu podrzedne)

  8. Tak działa Opera (nie ładuje obrazków które są w kontenerze z display: none). A obrazki są maluśkie… lecz postaram się zmienić mój sposób Image Replacement na bardziej dostępny, jak obrazki się nie ładują - ale to w następnym poście.

  9. "Jak Opera to tylko 9.0, sorry… to nie warunki rządowo-terminalowe z Win 95 i IE 4.0 na pokładzie. Polecam zrobić upgrade."

    jeśli chodzi o mnie to to Twoje menu zadziała mi za jakieś pół roku kiedy zdecyduję, że czas zainstalować Operę. Moim skromnym zdaniem robi tak większość osób.

  10. Jak na razie z Opery 9 korzysta jedna trzecia operowiczów. A menu jest nawet bardzo fajne.

  11. Riddle - a gdzie ta dostępność, o której piszesz? Jeśli masz na myśli tekst, który potem potraktowałeś visibility:hidden to możesz się rozczarować. Niektóre czytniki (screen readers) taki tekst po prostu ignorują (bo skoro jest niewidoczny, to po co go czytać) i tym sposobem dostępność bierze w łeb.
    Lektura na wieczór: http://www.alistapart.com/articles/fir/

    Poza tym faktycznie irytuje ten znikający drugi poziom. W obecnej wersji menu na DT jest tak, że jak już jestem w Battle>Training to mogę bez zbędnych ruchów myszy przejść do Battle>Upgrades. A tak muszę podświetlić Battle po czym ostrożnie (żeby nie wyjechać za obszar) przesunąć kursor na wybraną opcję.

    OT: jak to jest - kiedy w Operze pojawią się błędy bezpieczeństwa, to koledzy krzyczą, że w nowej wersji ich nie ma (ostatnio była konieczna przesiadka z 8.54 na 9). Ale jak coś nie działa (vide ww. menu) w tej dziurawej starej wersji to już jest raban, że się kogoś ignoruje, i w ogóle tak nie można itd. Ale to tylko taka uwaga na marginesie… ;)

  12. Yano, to nie jest gotowe menu do wdrożenia na stronie. Przeczytaj dokładnie tekst postu i moje komentarze.

  13. Przeczytałem i znajduję tylko informację, że jest możliwa dalsza modyfikacja. Nie znalazłem natomiast nic, że jest to pierwsza część. A modyfikować można wszystko na wiele sposobów, więc to info w sumie nic nie daje. Ale niech Ci będzie, że się czepiam. ;)

    A tak przy okazji – coś mi nie gra w tytule notki. Niby jest OK, ale jakoś taki dziwny dysonans mam.

  14. piotr potera 14 08 lipca 2006, 04:43

    Riddle, odwalasz swietna robote. Jak ty znajdujesz na to wszystko czas?...Ja swoj artykul o beztabelkowym layoucie pisze juz drugi miesiac ;-)

  15. Radziłbym Ci, a także innym, używać w text indent wartości mniejszych niż -1000px. Istnieją już monitory o szerokości obrazu znacznie większej niż 1680, a już na 1680 tekst wyłazi z lewej strony.

  16. Czemu nie moge nawigować klawiaturą? :(
    Mimo to, nice try.

  17. Paweł Majczyk: Good point, naprawię to w nast. wersji.

    Michał Stempień: Na to też trzeba znaleźć rozwiązanie… najlepiej ograniczyć szerokość strony i dać kolor tekstu równy kolorowi tła (o ile to możliwe). Albo overflow: hidden?

  18. -1000px? a nie lepiej w em-ach?

  19. Menu jest niedostępne przy wyłączonych obrazkach. Proponuję użyć innej metody IR.

  20. bogi: ameryki nie odkryleś, ale wskazać możesz jakąś lepszą alternatywę? W tym problem, że jej nie ma. Mimo, że nie wielka ilość użytkowników ma wyłączone tła i właczone arkusze styli jednocześnie, to faktycznie problem istnieje i nie jedna tęga głowa nad tym myśli. Więc możesz sobie proponować coś innego do woli, o ile coś takiego jest.

  21. Paweł, ależ jest. :) http://mezzoblue.com/tests/revised-image-replacement/ przejrzyj. :) Nie wiem jeszcze co z zachowaniem przestrzeni określanej przez tekst, ale ostatni przykład z width/height: 100% wydaje się być ok…

  22. IR staram się unikać, kiedy tylko możliwe, bo nie ma idealnej metody. Jednak jeśli już muszę, to korzystam z trochę zmodyfikowanej na swoje potrzeby tzw. Gilder/Levin Method http://mezzoblue.com/tests/revised-image-replacement/

    Daleko niedoskonała metoda (nadmiarowy span to daleko nie najistotniejsza wada), jednak ze wszystkich dostępnych wykorzystujących tylko CSS IMHO najlepsza.

    Co do ilości użytkowników mających wyłączone obrazki, a włączoną obsługę CSS, to nie byłbym taki pewien, że jest ich tak niewiele (użytkownicy GRPS). Jestem skłonny zaryzykować twierdzenie, że może ich być więcej niż użytkowników screenreaderów, speakerów, przeglądarek tekstowych etc. I dlatego, jeśli ktoś stawia sobie za cel dostępność, to zadbanie o dostępność kluczowego przecież elementu witryny, jakim jest menu, dla użytkowników z wyłączonymi obrazkami powinno być jedną z podstawowych kwestii.

  23. Co do small screen renderingu (cholera, nigdy nie wiem kiedy apostrof, kiedy myślnik, a kiedy bezpośrednio) to wszystko powinno się rozwiązywać na etapie media="handheld". Artykuł Dave'a czytałem już kiedyś, jednak nie przekonuje mnie to - nie wiem czemu, ale mam wrażenie, że kiedyś przez takie zabiegi obudzimy się 'z ręką w nocniku'. To już eksperymenty takie jak hacki do IE zastąpione komentarzami warunkowymi. Chyba łatwiej byłoby uświadomić tych, co wyłączają obrazki w tle, aby do css użytkownika dopisali li * {text-indent: 0;} niż szukać jakiegoś remedium na wszystkie problemy.

  24. Paweł,

    pisząc o użytkownikach GPRS wcale nie miałem na myśli tych od handheldów, tylko zwykłych użytkowników laptopów łączących się z Siecią za pomocą GPRS i korzystających z popularnych przeglądarek. A dla nich, jak wiadomo, liczy się każdy bajt i często wyłączają obsługę grafiki w ogóle.

  25. No to musi się liczyć z tym, że coś może nie działać jak powinno, bądź wprowadzić w życie li * {text-indent: 0}. Naprawde, jeżeli ktoś się uprze to nie obejży strony i tak - wówczas jesteśmy bezsilni.

    Właściciel laptopa łaczący się przez GPRS? :) No nie wiem czy przejmuje się takimi drobiazgami jak koszty połączenia, tymbardziej jak wydaje te kase na gre w jakieś MPOG z interfejsem www. Nepewno jakiś procent, ale.. no właśnie, albo jest sie sprytnym, albo bogatym, albo wiatr w oczy wieje.

  26. Spoko wiecie...
    Też robie4 sama szablony xD :*
    A to co tu piszecie to mi się przyda :*
    Kiedyś .... xD

  27. A nie mozna zrobic takiego menu tak, zeby po kliknieciu gornego taba dolny zostawal ? :P Przynajmniej nie trzebaby bylo wyczyniac cyrkow z wcelowaniem w dolny poziom.

  28. Bawiłem się trochę potem CSS i JS i powstał krok 5, ale nie jest on jeszcze gotowy, żeby o nim napisać.

  29. Ale zostajace taby mozna zrobic na golym CSSie ;] I kto powiedzial, ze CSS jest statyczny ? :P

  30. Wszystko pięknie tyle tylko ze twoj przyklad na moim IE6 nie dziala…
    za to ten dziala: http://www.xs4all.nl/~peterned/examples/cssmenu.html

    tylko co z tego jak proboje wrzucic do styli u siebie linijke behavior w body i nic to nie daje :(

  31. U mnie też pod IE6 nie działa ten przykład – nie widać podmenu co sugeruje, że hover nie działa, ale manipulując przy stylach i dając display:block zamiast none podmenu sie pojawilo ale nierozciągniete i poprzestawiane… :(

Dodaj komentarz

Do formatowania komentarzy używaj Textile (HTML nie działa). Szczególnie jeśli wklejasz większe fragmenty kodu. W razie niepewności użyj podglądu komentarza.

Wypowiedzi obraźliwe, infantylne oraz nie na temat będą moderowane – pisząc postaraj się zwiększyć wartość dyskusji.

Komentarze nie służą do wysyłania wiadomości albo informowania o błędach, itd. Chcesz coś mi napisać – skontaktuj się.