Inteligentne menu w CSS

Ostatnio wszelkiego rodzaju menu zbudowane wyłącznie z użyciem CSS'a stały się popularne.Po co używać JavaScriptu jeśli te same możliwości daje na sam CSS? No właśnie. CSS w połączeniu z grafiką też ma duże możliwości. Jednak zauważyłem, że minusem takich rozwiązań jest konieczność rezygnacji z płynnej dynamiczności. Nie spotkałem do tej pory menu CSS które mogłoby przykładowo pochwalić się płynnym przejściem tła z jednego elementu do drugiego.
Postanowiłem sprawdzić czy uda mi się ten problem jakoś rozwiązać...

I tak powstało to: dynamiczne menu z płynnym efektem przejścia.

Info: Jeśli zaczynasz przygodę z CSSem to przedstawione rozwiązanie raczej nie jest dla Ciebie. Nie nadaje się ono także do tworzonych projektów. Potraktuj ten wpis jako zwykłą ciekawostkę. Jeśli uczysz się projektować menu w CSS to zacznij od prostych przykładów - ten jest mocno zagmatwany.
Jeśli szukasz efektownego menu na swoją stronę, to sprawdź to: Flash? Nie, dziękuje - nietypowe menu w jQuery"

Przykład doskonałości to nie jest (nadmariarowe tagi, etc), ale przynajmniej pokazuje że wykorzystując CSS'a oraz prostą grafikę da się zrobić coś bez użycia JavaScriptu czy Flasha.
Szkoda tylko, że przykład ten działa jedynie w przeglądarkach Firefox i Opera - mimo tego zdecydowana większość z Was może obejrzeć jak to działa.
Safari i IE jak na chwilę obecną sobie z tym nie radzi (albo ja sobie z tym nie radzę - jak kto woli).

W tym wpisie postaram się przynajmniej częściowo wyjaśnić jak to menu działa i w jaki sposób udało mi się pokonać problemy które co chwilę pojawiały się przy budowaniu 'tego czegoś'.

Jak to działa?

Założenia są proste: mamy kilka elementów <a> wewnątrz których znajdują się elementy <span> ułożone według powyższego schematu.

W zależności od tego, na który element trafi kursor, element ten zmienia swoje właściwości: zmianie ulega jego rozmiar i pozycja, a także tło. Tłem aktywnego elementu staje się wcześniej wczytana (o tym później) animacja gif która symuluje przejście z poprzedniego elementu <a> do obecnie aktywnego. Najlepiej sobie to wyobrazić patrząc na poniższy obrazek:

W tym przypadku mamy do czynienia z przejściem z elementu menu 'kontakt' na 'portfolio'.Kursor opuszczając sąsiadujący element <a> przemieszcza się na docelowy element uaktywniając <span class="right">. Teraz ten span rozciąga się na dwa elementy <a>. Animacja w tle symuluje przejście.

Elementy <span> nie wypełniają elementu nadrzędnego całkowicie - istnieje wolna przestrzeń od góry oraz od dołu która jest wykorzystywana do rozpoznawania sytuacji kiedy kursor trafia na menu po raz pierwszy (o tym w dalszej części).

Istotną sprawą w tym menu jest pozycjonowanie elementów.
<span> z animowanym tłem musi znajdować się nad elementem docelowym, ale jednocześnie pod elementem poprzednio aktywnym. Taka sama zasada musi być zachowana niezależnie od tego z której strony ma nastąpić symulacja przejścia.

Przeglądając kod źródłowy menu zauważymy pierwsze semantyczne 'minusiki' rozwiązania. Puste elementy można wyeliminować, jednak napotkamy wówczas kolejne problemy (być może na nie też istnieje rozwiązanie) - nic nie stoi na przeszkodzie aby samemu trochę poeksperymentować.

Bardziej szczegółowy opis działania: analiza CSS

W dalszej części wpisu konieczny już będzie podgląd źródła przykładu oraz pliku style.css. Całość można pobrać klikając tutaj
Kolejne istotne fragmenty pliku .css zostały oznaczone symbolami w komentarzach - będę się tylko tutaj do nich odwoływał w celu zachowania czytelności.

A - wygląd ogólny:

Ustalenie układu blokowego menu, nadanie tła, rozmiarów, itd.
W tym przypadku menu ma wysokość 32px, do tego dołożyłem 6px (cel tego opisany dalej) - razem 38px.

B - marginesy:

Jak już wcześniej wspomniałem elementy <span> nie wypełniają całego bloku <a>. Potrzebna jest wolna przestrzeń która 'przechwyci' kursor gdy będzie on uaktywniał pierwszy wskazany element.
Gdy kursor aktywuje dany element <a> to następuje usunięcie elementów <span> oraz nadanie tła (hover.gif: płynne pojawienia się podświetlenia bez symulacji przejścia).
Dzięki temu w obrębie aktywnego elementu nie nastąpią już żadne zdarzenia.

Dalej widzimy określenie pozycji różnych elementów (w tym ostatniego) w elemencie nadrzędnym aby utworzyć omawiany margines.

C - z-index:

Aby elementy menu odpowiednio się na siebie nakładały musiałem przypisać im kolejną pozycję w hierarchii warstw. Aktywny element znajdzie się pod innymi.

D - span:

Określenie położenia elementów nieaktywnych, oraz aktywnych <span> zgodnie z wcześniejszym opisem i szkicami.

E - generowana zawartość

Na poniższym obrazku widać, że brakuje tutaj tekstu 'Portfolio' - został on zasłonięty aktywnym elementem <span>. Dokładnie mówiąc to on był cały czas zasłonięty, tylko że teraz element <span> stał się nieprzeźroczysty z powodu animowanego tła.

Ten problem trzeba było jakoś rozwiązać - ja wybrałem generowaną zawartość w CSS. Dzięki temu uniknąłem kolejnych elementów w <a> które by całą sprawę tylko skomplikowały.

Na początku zdefiniowałem treść generowanej zawartości która będzie tworzona tylko wówczas, gdy zostanie aktywowany którykolwiek z elementów <span> (zasłaniających napis). Nie wiem czy istnieje możliwość pobrania treści z elementu nadrzędnego podobnie jak to jest z attr(title).
Osoby które uważają wstawianie czystej treści do styli css za nieuzasadnione, mogą użyć attr(title) pamiętając o odpowiednim wyedytowaniu kodu HTML.

W dalszej części kodu odpowiedzialnego za generowaną zawartość określiłem że będzie ona wyświetlana jako element blokowy. Padding posłużył mi do odpowiedniego ustawienia napisu (należy pamiętać że mamy tutaj do czynienia ze zmienionymi rozmiarami ze względu na aktywację elementu nadrzędnego).

F - focus

Standardowo po kliknięciu na elemencie menu, przerywana ramka wokół linka obejmowałaby dwa elementy - to wygląda moim zdaniem trochę nieestetycznie. Postanowiłem więc całkowicie usunąć focus z tego menu.

Ktoś może powiedzieć, że mogłem użyć tego kodu aby rozwiązać problem z focusem na dwóch elementach:

a:focus {  overflow: hidden;}

Taki kod spowodowałby, że ramka focus zawsze pojawiałaby się tylko na jednym elemencie - o to chodzi.
Powyższy kod można swobodnie stosować w swoich projektach - dzięki niemu klikając przykładowo na logo mojego bloga pojawi się ramka focusa w odpowiednich rozmiarach. Bez niej rozciągałaby się aż do brzegu przeglądarki.

Ale dlaczego tego nie zastosowałem? W Firefoksie pojawiały się wówczas dziwne problemy z generowaną treścią. Klikając na wygenerowane słowa link nie działał (poza ich obszarem już tak).

Jeśli chodzi o użyteczność (użycie klawisza [Tab] w nawigacji), można zastosować sztuczna ramkę focus- coś na wzór tego:

border: dotted #000 1px;

Następna linia pliku CSS odpowiada za usuniecie dziwnego zachowania Opery dzięki któremu tło zaczynało migać.

G - wczytanie plików .gif przy ładowaniu

Pod koniec tworzenia menu napotkałem problem który wydawał mi się nie mieć rozwiązania.
Jak wiadomo tworząc menu, tło aktywnego linka które określiliśmy poprzez :hover wczyta się dopiero przy aktywacji tego właśnie linka, nie wcześniej.

W przypadku takiego menu to byłby poważny problem estetyczny który moim zdaniem całkowicie by skreślał takie rozwiązanie.

Powstał problem: jak wczytać wcześniej tło gifa?
Przypomniał mi się wówczas pewien 'guru' zachodniej blogosfery który w podobnym przypadku powiedział że to niemożliwe.

Ja oczywiście zapominając o jednej ważnej rzeczy spróbowałem zrobić to tak - ustawiłem plik .gif jako tło innego elementu. Mimo tego że plik wczytywał się wcześniej takie rozwiązanie okazało się nieskuteczne - przeglądarki (tutaj Firefox i Opera) nie traktują powielonych gifów osobno. Jeden plik .gif wstawiony w dwóch miejscach będzie wyświetlany ramka po ramce w tej samej kolejności. Czyli niezapętlony plik .gif przestawał po prostu działać.

Musiałem stworzyć 'coś' co działałoby na tej zasadzie: osadzenie pliku .gif przy wczytywaniu strony, a następnie jego usunięcie w taki sposób, abym mógł wykorzystać wcześniej wczytany plik jako tło elementu <span>
...oczywiście wykorzystując jedynie CSS.

Wpadłem na taki pomysł: generowana zawartość po raz kolejny.

Wybrałem dowolne elementy <a>. Dzięki CSS dodałem generowaną treść której ustawiłem pliki .gif jako tło. Całość 'wyrzuciłem' poza widzialny obszar. Gdy kursor znajdzie się nad elementem <div id="menu"> wówczas następuje usunięcie tła z wygenerowanej zawartości, co jest równoznaczne z tym, że plik .gif przestaje być odtwarzany - jego ponowne wstawienie w jakiejkolwiek formie będzie oznaczało odtwarzanie od pierwszej klatki.

Cel został osiągnięty.

H - Safari oraz IE

Safari podobnie jak IE wyświetli animowany gif jedynie raz jeśli ten nie jest zapętlony (niezależnie od tego kiedy zostanie wczytany). To wyklucza możliwość użycia gifów w obecnej postaci. Myślę, że jednak ten problem dałoby się rozwiązać.
W Safari i IE pojawiły się natomiast inne problemy związane z podstawową zasadą działania menu: IE nie radzi sobie z pozycjonowaniem warstw, natomiast Safari z generowaną zawartością (migotanie kursora).

Dlatego też postanowiłem ukończyć menu w postaci takiej jak jest teraz. Poprawienie błędów w dwóch wymienionych przeglądarkach sprowadza się do całkowitego ukrycia elementów <span>.

Dla Safari jest to kod:

@media screen and (-webkit-min-device-pixel-ratio:0) {  #menu a span { display: none; }}

Dla IE należy natomiast zastosować komentarze warunkowe.

Użytkownicy tych dwóch przeglądarek zobaczą standardowe, działające menu jednak bez żadnych efektów przejścia.

Podsumowując:

Menu ma pokazywać możliwości CSS'a i pewne sposoby rozwiązywania określonych problemów.
Jeśli ktoś chciałby wykorzystać takie rozwiązanie na swojej stronie to należałoby pamiętać również o tym, aby menu nie powodowało problemów na innych przeglądarkach (poza czterema tutaj sprawdzonymi), nawet mimo tego, że ewentualne problemy mogłyby dotyczyć znikomej części użytkowników.

// To menu należy traktować raczej jako taką ciekawostkę, tak samo jak Homera w CSS - to dla tych którzy będą chcieli napisać coś w stylu 'po co to?', lub 'w IE nie działa' ;)

Jeszcze linki dla osób które nie czytały tego długiego wpisu tylko przewinęły stronę na koniec szukając przykładu:
- Demo - zobacz jak to działa
- Download - pobierz cały przykład

Komentarze 160:

  • » Adriano: 28.10.2008 o 13:21

    Z tego co widzę po logach to 'buforowanie' gifów działa wszystkim (obecnie Opera, Firefox).

  • » Tom: 28.10.2008 o 13:39

    Myślałem że gdzieś w pliku z CSS jest ukryty javascript :)
    Niezłe :)

  • » RoB: 28.10.2008 o 14:49

    Adriano, wrzuć to na Digga :)

  • » Adriano: 28.10.2008 o 15:02

    @RoB: Opis jest tylko po polsku to nic z tego raczej nie wyjdzie. Chyba że sam przykład pisząc w opisie co to w ogóle jest...

  • » bobiko: 28.10.2008 o 20:00

    No muszę powiedzieć że cholernie dobry pomysł :)

    Co do digga, to, Ci którzy znając choć troche css, zrozumieją od razu ;)

  • » Adriano: 28.10.2008 o 21:31

    Co do digga, to, Ci którzy znając choć troche css, zrozumieją od razu ;)

    Racja, jak ktoś chce to można wrzucić.
    Sam jestem ciekaw jak wyglądałby digg effect w przypadku mojego bloga ;)

  • » procek: 29.10.2008 o 21:44

    Szkoda, że nie napisałeś, że to mój pomysł z niezapętlonymi GIFami :P No, ale takiego zastosowania się nie spodziewałem. Mam inny pomysł użycia takich gifów - niebawem to opiszę :)

  • » Adriano: 29.10.2008 o 21:59

    Szkoda, że nie napisałeś, że to mój pomysł z niezapętlonymi GIFami :P

    Twój pomysł? Nie przesadzaj ;)
    Wpadłeś na to po tym jak Ci pokazałem moje (niedokończone jeszcze) menu z niezapętlonymi gifami właśnie... ;)

  • » Bogart: 09.01.2009 o 20:17

    Fajnie to zrobiłeś, ja parę razy się zabierałem za css-owe menu, ale zawsze dochodziłem do wniosku, że za dużo z tym roboty i kombinowania a o płynnym przechodzeniu nawet nie myślałem, żeby taki efekt uzyskać. Flesho'wych w ogóle nie robię.

  • » Sylwia: 23.01.2009 o 00:27

    No ładne się prezentuje - tak jak we flashu, chociaż ja jestem zwolenniczką prostych stron, nie kręcą mnie "bajery" ;) Niewątpliwie Twój sposób będzie bardzo przydatny dla osób, które pożądają takich efektów, a nie chcą stosować flasha ze względu na roboty wyszukiwarek. Widzę, że się ostatnio ostro wziąłeś za wynalazki css'owe ;) pozdrawiam

  • » Chris Trynkiewicz: 25.01.2009 o 02:22

    Podoba mi się, dobra robota :)
    W FF jednak jest zauważalny pewien mały błąd - czasem po jeżdżeniu po elementach n, n+2 (czyli co drugi, a nie po kolei) przejście robi się nie z tej klasy/spana/whatever, z której powinno.
    Przykład: 1-3-1-3-1-3, omijając 2 i 4. W pewnym momencie widać (przynajmniej u mnie), że jest slide z numeru 4 na 3 - skąd?

  • » Wojtek: 09.02.2009 o 19:47

    Dla IE należy natomiast zastosować komentarze warunkowe.

    Czyli jakby to miało wyglądać?

  • » Adriano: 09.02.2009 o 21:52

    Np tak:

    <!--[if IE]>
    <link href="ie.css" rel="stylesheet" />
    <![endif]-->

    Poszukaj - w sieci jest o tym dużo informacji.

  • » Adriano: 09.02.2009 o 21:54

    Chris Trynkiewicz: trafiasz na mały margines. To takie niedociągnięcie, ale spełnia swoje funkcje.

  • » KMO: 04.04.2009 o 15:08

    Naprawdę ciekawe rozwiązanie. Jestem pod wrażeniem.

  • » Adamiel: 25.06.2009 o 17:42

    Co do hacków IE (jak podał Adriano) to najlepiej sprawdzić na różnych wersjach IE, bo najnowsze IE interpretują praktycznie, a przynajmniej prawie tak samo jak FF, czy Opera, a w starszych może się rozjeżdżać. IE hacki pozwalają wybrać dla jakiej wersji Internet Explorera ma być dany kod/komenda.

  • » krysiek: 03.04.2010 o 09:19

    Zastanawiam się tylko ile osób bawi się w robienie własnych stron, opartych o swój projekt. Większość stron w tym momencie to jest taki lubi inny cms ( nie mówie tylko o tych spamowych). Co więcej nie wiem czy jest aż taki sens w tworzeniu fajnego designu. W zasadzie poza różnego rodzaju konkursami nie jest to aż tak bardzo przydatne. Zwykły użytkownik potrzebuje raczej czarno na białym tekstu i przejrzystego układu menu, bez zbędnych najerów.

  • » m1chu: 22.07.2010 o 17:46

    Wprost przeciwnie. Zwykłego użytkownika to właśnie dobry, a zarazem użyteczny design przyciągnie najpierw do serwisu, a nie niewiadomo jakie zaimplementowane funkcje, o których istnieniu, czy działaniu nie będzie miał pojęcia przy pierwszym wejściu na stronę. Mnie osobiście techblog Adriano przyciągnął właśnie wyglądem.

    Co do komentarzy warunkowych, jakiś czas temu opisałem je w miare konkretnie: http://m1chu.eu/2008/12/22/haki-css-dla-niepokornego-internet-explorera/

  • » Warchol: 04.10.2010 o 07:57

    Bardzo fajne menu, szkoda tylko ze nie dziala w ie ;/ znaczy wszystkie funkcje bo tak to lata :)

Dodaj komentarz:

Dostępne tagi: [link]http://adres-www[/link] [quote]cytat[/quote] [code]kod[/code] [pre]tekst preformowany[/pre] [b]bold[/b]