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.

Czasami zdarza się tak, że oddając gotową stronę (zarządzaną zazwyczaj przez panel administracyjny) nie przewidzimy dokładnie ile danych klient będzie chciał w nią wstawić. Chyba najbardziej wrażliwą częścią strony jest nawigacja - złożona z listy nieuporządkowanej, i floatowanych pozycji.

Istnieją dwa podejścia do zaprojektowania takiego menu - pierwsze to określić na stałe szerokość elementu, a drugie to pozwolić aby szerokość była kształtowana przez długość tekstu. Oba mogą zawieść, jeśli potrzeba dłuższej etykietki niż tej w pliku PSD. Dlatego postanowiłem wykorzystać możliwości CSS2 i użyć min & max-width do osiągnięcia rozsądnego kompromisu.

Projekt pozycji nawigacji określonych szerokością minimalną i maksymalną nie jest trudny sam w sobie - tak naprawdę sterują nim dwie właściwości i tyle. Lecz musimy pamiętać, że to tylko początek XXI wieku, a przeglądarki kochają wręcz stroić fochy. Po kolei jednak:

Podstawowe menu

HTML menu.

  1. <ul>
  2. <li><a href=""><span>…</span></a></li>
  3. <li><a href=""><span>…</span></a></li>
  4. </ul>

Z pomocą stylów prezentacyjnych (które można dowolnie zmieniać, stąd nie piszę o nich dokładnie) otrzymałem takie menu. Na marginesie - zagnieżdżone spany możemy pominąć, ale wtedy będzie trzeba zmodyfikować hack na IE poniżej.

Następnie wypozycjonowałem je na ekranie, elementy dostały float: left, a linki wyświetlanie blokowe.

  1. ul {
  2. margin-left: auto;
  3. margin-right: auto;
  4. width: 760px;
  5. overflow: hidden;
  6. }
  7. ul li { float: left; }
  8. ul a { display: block; }
Step 02 Preview

Efekt użycia tych reguł widać w następnym podglądzie. Ujawnia on problem, o którym pisałem na początku – za długie etykietki powodują brzydkie rozepchnięcie menu.

Założyłem więc, że potrzebujemy na nie więcej miejsca i postanowiłem powiększyć wysokość dodając kolejną linię – choć oczywiście możemy poprzestać na jednej, a wystający tekst skrócić.

  1. ul a {
  2. display: block;
  3. min-height: 3em;
  4. min-width: 80px;
  5. max-width: 105px;
  6. }

Błędy - Opera, Safari

To by było na tyle – dla przeglądarek, które wspierają użyte w tym selektorze standardy. Jest to Firefox i… Internet Explorer 7. Hehe, też się zaśmiałem. Opera 9, Explorer 6 i Safari 2 oraz 3 mają problemy (każdy program inne), stąd potrzeba pohakowania kodu.

Safari bug

Safari ma nie do końca poprawną implementację max-width. Otóż przeglądarka ta pobiera szerokość tekstu przed zawinięciem i w stosunku do niej aplikuje CSS-owe właściwości. Ostatnia pozycja ma mniej więcej 150px bez zawinięcia, max-width jest 105px, więc ogranicza szerokość, a tekst jest zawijany. To co powinna później zrobić jest dość proste do przewidzenia - sprawdzić jeszcze raz szerokość i zobaczyć, że teraz wynosi jakieś 70px i pozwolić na to. Błąd ten jest bardzo podobny do tego popełnionego przeze mnie podczas pisania expression dla IE6, ale o tym za chwilę.

Opera bug

Opera przesuwa każdą pozycję w lewo o jakąś wartość. Okazuje się, że aby naprawić to dziwaczne zachowanie należy zmienić display linków na inline-block.

  • Wtedy natomiast Firefox strzeli focha - dla niego dopisałem display: -moz-inline-block, wystarczające na ten moment.

  • Przez zamianę wyświetlania na liniowo-blokowe Safari 2 musi dostać łatkę na położenie w pionie.

  • A jako, że zepsułem trochę kod (przez Operę, wiedziałem! ;)), dla IE7 trzeba przywrócić display: block.

  1. ul a {
  2. display: inline-block;
  3. display: -moz-inline-block;
  4. min-height: 3em;
  5. min-width: 80px;
  6. max-width: 105px;
  7. vertical-align: top;
  8. }

Zrobione.

Internet Explorer 6

Ok, idziemy dalej. Mamy przed sobą Explorera 6, bez którego eksplorowanie komercyjnych rynków jest niemożliwe – stąd musiałem znaleźć rozwiązanie. Z pomocą przyszły mi expressions i trochę dedykowanego CSS-a.

  1. ul { zoom: 1; }
  2. ul a {
  3. height: 3em;
  4. display: block;
  5. float: left;
  6. }
  7. ul a span {
  8. float: left;
  9. cursor: pointer;
  10. }

Pierwsza linijka włącza layout. Następna reguła to proste poprawki - height zamiast min-height, wspomniane wyświetlanie blokowe oraz dodatkowy float (hasLayout powoduje, że linki miałyby 100% szerokości).

Trzecia reguła wykorzystuje wewnętrzny span, aby później można było określić faktyczną szerokość tekstu pozycji menu. Dalej - kursor często jest źle interpretowany przy zabawach z hasLayout, dlatego też łatka.

Wreszcie - expression (wstawiany do ul a { … }):

  1. hackwidth: expression(
  2. this.parsed ? 0 : (
  3. boxpl = parseInt(this.currentStyle.paddingLeft),
  4. boxpr = parseInt(this.currentStyle.paddingRight),
  5. boxbl = (this.currentStyle.borderLeftWidth == 'medium') ? 0 : parseInt(this.currentStyle.borderLeftWidth),
  6. boxbr = (this.currentStyle.borderRightWidth == 'medium') ? 0 : parseInt(this.currentStyle.borderRightWidth),
  7. this.oldwidth = (this.offsetWidth - (boxpl + boxpr + boxbl + boxbr)),
  8. maxwidth = (this.oldwidth > 105) ? (this.style.width = '105px') : 0,
  9. minwidth = ((this.oldwidth < 80) || (this.firstChild.offsetWidth < 80)) ? (this.style.width = '80px') : 0,
  10. this.parsed = 1
  11. )
  12. );

Bez paniki. ;) Pierwsze pięć wewnętrznych linijek odpowiada za pobranie wszelkich paddingów i obramowań, aby odjąć je potem od szerokości elementu (offsetWidth).

Później zapisuję w wymyślonym atrybucie oldwidth tą szerokość, aby dalej porównać ją z wartościami, ustawionymi jako min/max-width. Dodatkowo, aby nie powielić błędu Safari dodałem warunek sprawdzający szerokość spana (this.firstChild.offsetWidth) po zawinięciu wiersza.

Finał

Step 5 Preview

Końcowy efekt jest prawie idealny - działa w miażdżącej większości przeglądarek i z powodzeniem może być stosowany wszędzie. Mam nadzieję, że się przyda - nie mogłem znaleźć lepszego (czyt: ani jednego) rozwiązania w Internecie. :)

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 22 czerwca 2007 o 23:36

Kategorie: CSS, Internet Explorer

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. Ciekawe, ciekawe, może być przydatne

  2. Gdyby to był wykop – wcisnąłbym „+1” :)

  3. ciekawy artykuł, śliczniusie menu ;)
    Ile to komplikacji może sobie człowiek narobić, przy ‘zwykłym menu’...

  4. another: Już możesz, wykopane.

    Świetna sprawa, ale ile to trzeba nerwów, żeby to wszystko bezbłędnie wszędzie działało, ech…

  5. [Walker]: „another: Już możesz, wykopane.”

    Dzięki że dodałeś :) Daję „wykop”

  6. Walker: Trzeba 6h dokładnie i setek wywalonych o expressions instancji IE6. :D Dzięki za Wykop.

    BTW: Gdyby ktoś nie kumał kontekstu etykietek w przykładach – zapraszam. :)

  7. Trzeba 6h dokładnie i setek wywalonych o expressions instancji IE6. :D

    A Ty to jeszcze kochasz ;).

    Dzięki za Wykop.

    No nie ma za co, należało się.

  8. Konqueror 3.5.7: dwie linijki ma menu,

  9. Rafał: A u mnie zarówno pod Firefoxem 2.0.4 jak i Konquerorem 3.5.7 jest tak samo.

  10. niech ktos jeszcze wymysli jak zrobic menu na 100% szerokosci i z rownymi odstepami miedzy opcjami. oczywiscie wszystko ma byc elastyczne. cos jak menu na tabelkach, ale bez tabelek.

  11. Riddle … jesteś wieki! ;)

    Muszę chyba zrozumieć expression, z tego co widzę bez znajomości tego tematu życie w zgodzie z wszystkimi przeglądarkami jest a wykonalne ;)

    dzięki wielkie ;)

  12. (Komentarz zmodyfikowany 23.06.2007 o 12:51)

    Na blogu Riddla mógłby być przycisk „wykop”.

  13. (Komentarz zmodyfikowany 23.06.2007 o 12:51)

    Kiedyś zdajsie był, ale zbluzgali go za to.

  14. Wykopałem i zdelicjowałem ;)

  15. Firefox Minefield a6pre „dzisiejszy”

    Menu wygląda prawie jak http://riddle.pl/-/xhtml/css-minmax-navigation/step-02/

    Prawie bo pozycja ostatniego menu, jest zawsze pod resztą, a zmieniając wielkość czcionki tylko się to menu przesuwa w poziomie.

  16. Musieli wywalić -moz-inline-block. Mam nadzieję, że w finalu poprawią, bo będzie trzeba przenieść display: inline-block dla Opery w JavaScript.

  17. Tylko taka mała sprawa: gdy w IE wyłączymy „Obsługę aktywnych skryptów” (czyt. Javascript), np. ze względów bezpieczeństwa, to wszystkie expressions szlag trafi.

    I znowu uważam, że dla IE to to jest przerost formy nad treścią, czyt. za trudne :P

  18. Nikt nie powiedział, że webdev frontendu jest prosty. To, że expressions działają tylko przy włączonym JS już mówiłem i mówiłem też dlaczego mam to w nosie. Ale chętnie posłucham jak Ty zrobiłbyś takie menu. :]

  19. Poza tym expressions chyba wywołują ten żółty paseczek że komputer jest w wielkim zagrożeniu ze strony tej strony;) I jak tu takie coś mojej mamie pokazać? ;)

  20. Tylko lokalnie. Online nie ma żadnego paska.

  21. Riddle: ...nie poziome, a pionowe? :P Wtedy byłby o wiele mniejszy problem z takim IE (height: 1% i takie tam).
    Albo sztywny width, zawijanie wierszy i już :D

    @up: LOL

  22. @Riddle O, a to mi udzieliłeś w tym momencie bardzo użytecznej informacji. Dzięki

    BTW: No k..wa – comment spam na Joggerze;/

  23. Bigismall 23 23 czerwca 2007, 19:56

    Ile to się trzeba naszarpać, przez lenistwo twórców przeglądarek.

  24. Ciekawy temat. W kilku projektach wykorzystywałem poziome menu nawigacyjne. Zdecydowanie irytuje mnie taka zabawa z IE, które niepoprawnie interpretuje CSS. Cóż pozostaje nam czekać na kolejną wersję badziewia z Redmond lub liczyć, że nagle ludzie zaczną korzystać z odpowiednich przeglądarek.

  25. Ja od jakiegoś czasu takie menu robię poprzez display:table dla standardowych przeglądarek i display:inline dla IE – wszystko ładnie się centruje (jeśli potrzeba) i dostosowuje do rozmiaru elementu, jeśli brakuje miejsca teksty w menu się zawiją w kilka linii..
    to powyżej wydaje mi się niepotrzebnie zagmatwane (nawet nie wnikałem w szczegóły)

  26. A może powinieneś, bo to co Ty robisz ma diametralnie inne podejście do zagadnienia nawigacji.

    Powtórzę raz jeszcze – expression to kod JScript z ograniczeniami składni wstawiony do CSS-a, który zapewnia mu najszybsze parsowanie jakie jest możliwe.

  27. hmm.., Opere mozna obejsc definiujac min/max-width dla li – Opera bierze szerokość dla li od spana, zatem nakladając jej przedzial [min/max]-width+padding+border odnosnika, obliczanie szerokosci elementow listy zaczyna dzialac prawidłowo – inline-block z glowy

    IE6 – moze renderowanie wizulane nie jest juz identyczne jak w pozostalych przegladarkach, ale definiujac tylko wysokosc, w jakis tam sposob calosc miesci sie w jednym ‘wierszu’ – choc w pewnym stopniu jest to alternatywa dla expressions
    ul { margin: 100px auto 0; padding: 3px; width: 760px; background-color: #ffc; border: 5px solid #f77; overflow: hidden;}
    ul li { float: left; min-width: 94px; max-width: 119px; line-height: 1.5em; }
    ul a { display: block; vertical-align: top; min-height: 3em; min-width: 80px; max-width: 105px; float: left; background-color: #f77; color: #fff; padding: 4px 6px; border-right: 2px solid #d00; }
    * html ul a { height: 3em; }
    ul a:hover { background-color: #c00; }

  28. Ech, to rozwiązanie ma niestety jedną bardzo poważną wadę. W momencie, gdy menu nie mieści się w szerokości, zaczyna rozchrzaniać cały design (czyt.: wyłazić poza contener). Doliczmy do tego fakt, że tak skonstruowane menu za Chiny ludowe się nie chce wyśrodkować (przynajmniej mnie się nie udało, nie ukrywam, że nie jestem specem od CSS’a), nawet na tej najsłuszniejszej przeglądarce ;).

    Osobiście zastosowałem zgoła inne rozwiązanie. Ma ono inny cel (wyświetla się inaczej niż powyższe, ale o to mi chodziło), ale moim zdaniem zachowuje się lepiej. Zasada działania:

    ul
    { text-align: center; /* Pięknie wyśrodkowuje całość */ line-height: 300%; /* Bez tego elementy menu „wychodzą” poza container, nie obrażę się jak ktoś napisze dlaczego tak się dzieje i jak to zrobić lepiej, żeby nie wstawiać na stałe 300% – zresztą powoduje to lekkie „przerwy” pomiędzy liniami menu */
    }
    li
    { display: inline; /* Zapewnia wyświetlanie prawie jak zwykły tekst */ list-style-type: none; /* pozbawiamy się nikomu do nieczego nie potrzebnych kropek */
    }
    li a, li a:link, li:a:active, li a:visited
    { white-space: nowrap; /* dzięki temu element menu nie rozjeżdża nam się na dwie linijki */ padding: 10px 20px; /* tu się zaczynają schody z wyżej wspomnianym line-height – jak ktoś poda lepszy sposób, by to ładnie wyglądało, to bardzo fajnie :) */
    }
    li a:hover
    { white-space: nowrap; /* jak wyżej */ padding: 10px 20px; /* jak wyżej */ background-color: #fff9bd; /* a taki przykładowy kolorek, byle inny niż tło, żeby było widać, że najechane i gdzie się kończy padding */
    }

    Problemy, jakie to wywołuje:
    Fx – brak danych, chyba wszystko w porządku :)
    IE7 – gubi padding-left dla pierwszego elementu drugiej (być może również każdej następnej) linijki menu. Nie mam pojęcia dlaczego.
    IE6 – nie mam jak przetestować
    Opera (9.21) – W ogóle nie zawija menu, wyłazi poza obszar contenera, jeśli się nie mieści

    Testowe zastosowanie powyższej metody można sobie obejrzeć pod tym adresem jeśli tylko będę miał kompa włączonego. Całość strony jest cały czas rozwojowa, jakieś tam testy itp., więc proszę się nie przejmować tylko oglądać menu :P

    Gdyby ktoś znalazł rozwiązanie powyższych problemów lub przetestował pod IE 6 (którego nie posiadam), to nie obraziłbym się, jakby coś napisał :)

    P.S. Dlaczego wszystko w nawiasach ignoruje znaki nowej linii?

  29. P.P.S. Z relacji Ktosia wynika, że w IE6 zachowuje się dokładnie tak samo jak w IE7.

  30. Mamy pytanie nie bezpośrednio związane z tym wpisem, ale…
    Jak zmienić rozmiar tła, które jest osadzone w DIVie?
    background-size nie jest obsługiwany – jest na to jakiś sposób?

  31. Zwiększyć wymiary obrazka?

  32. 1) mamy diva o stałych wymiarach, 2) ten div ma tło, które jest przygotowane tak, żeby wypełniało całego diva (czyt. ma ten sam rozmiar, co div)

    Napotkałem taki problem podczas przeskalowywania (powiększania) strony – Opera zachowuje porządek i wszystko wygląda okej (cała strona zostaje przeskalowana) natomiast w Firefoksie powiększany jest tylko tekst (cały layout się sypie).

  33. webmajsta 33 04 lipca 2007, 22:56

    jeszcze jedna uwaga: predkosc dzialania expressions.. dla jednego elementu na stronie moze to i jest najszybsze, ale kiedy taki selektor odnosi sie np do 50*7=cale mnostwo komorek w tabeli to dwurdzeniowy proc zaczyna sie mocno grzac.

    uzywac z rozwaga, nie bez przyczyny expressions nie sa bardzo popularne na tzw. swiecie. sa potezne – prawda, nawet bardzo, ale wydajnosciowo nie jest najciekawiej.

    uzywac z rozwaga, bo mozna sie mooocno przejechac

  34. @webmajsta: Gdyby IE poprawnie obsługiwało CSS to mało kto używał by expressions…

  35. webmajsta 35 04 lipca 2007, 23:29

    ale ja o jednym, Ty o drugim

    mowie tylko, zeby sie nie zapowietrzyc od ‘potegi’ expressions, bo aplikacja, w ktorej ‘stare dobre’ haki cssowe zastapi sie expressions jest ‘politycznie czysta’ ale i czesto bardzo, bardzo wolna..

    jesli klient wymaga predkosci, a takich jest sporo, zwlaszcza takich, ktorzy budzety licza w milionach (czyli najlepszych mozliwych :) ), warto sie czasem przeprosic z tabelkami. z umiarem, rozwaga i pelna odpowiedzialnoscia, ale czasem sie to naprawde oplaca – pod kazdym wzgledem – czystosci kodu, utrzymania go, predkosci dzialania. czasem kuleje dostepnosc, ale sa to najczesciej strony firmowe/XXtranetowe, ktore i tak sa przeznaczone tylko dla pelnosprawnych i do ogladania na kompach w biurach.

    przyczyny dla ktorych expressions wogole pojawiaja sie jako temat rozmow na stronach webdeveloperskich sa chyba poza ‘scope’ tego postu, prawda? kazdy wie, ze IE jest be.. a raczej kazdy potrafi to powiedziec, najczesciej jednak na tym konczy sie wiedza takiego eksperta. bo juz konkretow to ‘nie pamietam’ :)

  36. Fajny art, ale mam pytanie: jak zrobic aby elementy listy (li) zajmowały całą długość <ul>. Mówiac krótko – chcę zlikwidować pusty odstęp w ostatnim elemencie listy. Na przykladzie to widac. Zreszta tyxczy sie to nietylko tego konkretnego przykladu, od jakiegos dluzszego czasu nie moge rozwiazac tego problemu tworzac wlasne menusy

    macie jakies pomysly?

  37. Możesz oszukać widza i dać tło takie samo jak w ostatnim elemencie listy, żeby się pokryło z tym bloczkiem.

  38. Niestety Safari 3 Beta ma jakies issues:
    http://img104.imageshack.us/img104/9162/picture22qo1.jpg

  39. Dzieki, ciekawy wpis :) Szkoda, ze takie brzydkie kolorki dobrales :P

  40. Ładnie opisane, na pewno się przyda.
    dzięki.

  41. A w Operze 9.27 wyświetla to menu z polami jedno pod drugim… Ciekawe jest to, że kiedyś to działało, a teraz coś znowu jest nie tak. Szlag człowieka może trafić, usiłuję zrobić podobne menu na własnej stronie a tu w FF i IE działa, aż miło popatrzeć, a w Operze, no niestety nie…

  42. Przepraszam, moja wina, wszystko działa jak powinno. Tak to jest jak się przestawia tryby wyświetlania stylów i się zapomina je spowrotem przestawić... :)

  43. a czy w operze 9.51 tez sie dobrze wyswietla? bo u mnie jest poprzestawiane.

  44. Trzeba poprawić dla Firefox 3 :P

  45. nowy IF8 sobie z tym nie radzi. A jakże by inaczej..

  46. szkoda, że p[od IE 8 nie dziąła :)

  47. Jeśli chodzi o poziome menu to pojawia się tam również problem ze środkowaniem jeśli nie znamy szerokości. Znalazłem jednak na to sposób:
    http://7pl.info/poziome-menu-zawsze-wysrodkowane.html

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