Standards versus reality

Przy obecnej popularności webdesignu (a raczej bycia trendy i walidacji, o czym pisałem w zeszłym miesiącu), co drugi użytkownik sieci cytuje z pamięci regułki CSS, sypie kodem (X)HTML, chwali się beztabelkowymi osiągnięciami. Często te beztabelkowce zajmują pięć razy więcej miejsca (ale przecież bandwidth is cheap) i w środku korzystają z display: table;, ale nie o tym chciałem pisać. Zajmijmy się problemami, w rozwiązaniu których standardy niewiele pomagają.

Jedną z rzeczy, którą można bardzo łatwo zaimplementować za pomocą semantycznego HTML + CSS jest układ kolumnowy. Używa się do tego dwóch elementów <div/>, z czego jeden jest floatowany do krawędzi.

<div id="navbar">
	<p>To jest menu</p>
</div>
<div id="content">
	<p>To jest treść strony</p>
</div>
#navbar
{
	margin: 0 10px;
	background-color: #69f;
	float: left;
	width: 180px;
	height: 400px;
	border: 1px solid #ccc;
}
#content
{
	width: 475px;
	border: 1px solid #ccc;
	background-color: #fff;
}

Problemy zaczynają się pod IE, który raz, że nie radzi sobie z precyzyjnym pozycjonowaniem floatów (jeśli floatowany element jest dokładnie szerokości dostępnego miejsca, to następuje złamanie wiersza, wymagany jest dwupikselowy zapas), a dwa, z nieznanych nikomu przyczyn magicznie podwaja marginesy wszystkich pływających elementów, jeśli tylko są typu block.

O ile jedynym rozwiązaniem na precyzyjne umiejscowienie dwóch kolumn jest zastąpienie obu bloków dwukolumnową tabelką, o tyle drugi problem rozwiązuje się trywialnie. Tyle, że nikt przy zdrowych zmysłach by na takie rozwiązanie nie wpadł. Należy mianowicie wymusić na przeglądarce traktowanie elementów floatowanych jako elementy typu inline. Wyjście takie wydaje się mocno abstrakcyjne i pozbawione sensu, ale działa, wobec tego ograniczymy jego skutki do jedynej słusznej przeglądarki, która obejść takich wymaga.

#navbar
{
	margin: 0 10px 0 0;
	background-color: #69f;
	float: left;
	width: 180px;
	height: 400px;
	border: 1px solid #ccc;
}
* html #navbar
{
	display: inline;
}

Trik polega na tym, że w IE nie działa właściwie nic, a przede wszystkim nie działają tam selektory CSS, z selektorem gwiazdki na czele. Powyższa konstrukcja mówi tyle, że display: inline; powinno odnosić się do wszystkich elementów o id równym navbar, zawierających się w elemencie <html/>, który ma dowolnego rodzica. Standard HTML wyraźnie precyzuje, że element główny, <html/> nie może mieć żadnego rodzica w ramach jednego dokumentu (nie dotyczy to drzewa DOM stron opartych na ramkach, ale tam CSS przypisywany jest do każdego węzła głównego i nie jest dziedziczony przez subdokumenty). IE uważa, że jest inaczej, co pozwala (przynajmniej do wersji 6.0) łatwo ukrywać workaroundy przed innymi przeglądarkami.

Oczywiście, bardziej eleganckim rozwiązaniem byłoby dodanie marginesu (równego szerokości lewej kolumny + pożądanemu odstępowi) do prawej kolumny:

#navbar
{
	background-color: #69f;
	float: left;
	width: 180px;
	height: 400px;
	border: 1px solid #ccc;
}
#content
{
	margin: 0 0 0 190px;
	width: 475px;
	border: 1px solid #ccc;
	background-color: #fff;
}

Nie rozwiązuje to jednak problemu układów bardziej skomplikowanych (gdzie stosujemy więcej niż dwie kolumny z floatem).

Drugi z częstych problemów, to zmuszenie elementów pływających do rozciągania rodzica. Weźmy pod uwagę prosty układ:

<div id="parent">
	<div id="child">
		<p>Lorem ipsum dolor sit amet
		consectetuer adipiscing elit.</p>
	</div>
	<p>Lorem ipsum, baby!</p>
</div>
#parent
{
	background: #fff;
	border: 1px solid #ccc;
	color: #000;
	padding: 5px;
}
#child
{
	background: #eee;
	border: 1px solid #ccc;
	color: #000;
	float: right;
	width: 100px;
}

Ze względu na stosunkowo długi tekst w pływającym boksie (w porównaniu do krótkiego tekstu głównego), istnieje ryzyko, że boks dziecka będzie dłuższy od boksa rodzica, co (zgodnie ze standardem) spowoduje jego wystawanie poza obiekt rodzica (nie zostanie od przedłużony). Nie jest to na ogół działanie, jakiego oczekujemy, pojawiło się więc kilka rozwiązań tej niedogodności.

Najpopularniejsze polega na wstawieniu, tuż przed tagiem zamykającym rodzica, następującego zapisu:

<br style="clear: both;">

Metoda ta ma jednak dwie wady: niepotrzebnie zaśmieca kod strony (co jest dla wielu do pominięcia) i powoduje pojawienie się zbędnego znaku złamania wiersza (co w zależności od pogody i populacji muflona, jest przez IE ignorowanie bądź nie).

Najczystsza metoda nie wymaga ingerencji w kod HTML i sprowadza się do dodania w arkuszu CSS (dla elementu rodzica) jednej linijki:

overflow: auto;

Przeglądarka od tego momentu zaczyna się interesować wystającymi elementami i elegancko przedłuży nam obiekt rodzica do wymaganego rozmiaru. Należy jednak uważać, bo gdy rodzic ma zadeklarowaną stałą wysokość, to w jego boksie pojawią się paski przewijania (uzyskamy efekt taki, jak przy elemencie <iframe/>.

Kolejny problem to wymuszenie minimalnej wysokości elementu. Najprościej byłoby napisać:

#element
{
	min-height: 300px;
}

Jest jednak przeglądarka, która w dziedzinie wspierania standardów nigdy nas nie zawiedzie. IE skrupulatnie ignoruje wszelkie zapisy tego typu. Co więcej, nieprawidłowo implementuje też atrybut height, który w IE zachowuje się właśnie jak min-height. Z pomocą przychodzi nam wspomniany wcześniej hack:

#element
{
	min-height: 300px;
}
* html #element
{
	height: 300px;
}

I kolejny problem: model boksa w IE prawidłowo został zaimplementowany dopiero w IE 6.0 i to tylko w trybie strict (w przeciwieństwie do domyślnego quirks mode). Powoduje to, że w przeglądarkach z Redmont padding nie wpływa na finalny rozmiar boksa. Innymi słowy, jeśli nasz obiekt ma szerokość zadeklarowaną na 100px i 20px paddingu z obu stron, to w IE zajmie dokładnie 100px, a na treść pozostanie 60px (po odjęciu 2x 20px), podczas gdy we wszystkich pozostałych przeglądarkach element zajmie 140px, rezerwując na treść całe (zadeklarowane w CSS) 100px.

Jeśli nie jest wymagana zgodność ze starszymi przeglądarkami (np. IE 5.5), to problem dla ciebie nie istnieje. Wystarczy zadeklarować prawidłowy DOCTYPE i wymusić przejście przeglądarki w strict mode. W biznesie webdesignowym rzadko można sobie pozwolić na tego typu uproszczenia, bo ważny jest każdy klient serwisu, niezależnie od tego, jak starej przeglądarki używa. Jedynym sensownym rozwiązaniem pozostaje symulowanie paddingu za pomocą zagnieżdżonych elementów i marginesów.

<div id="outer">
	<div id="inner">
		<p>Treść.</p>
	</div>
</div>
#outer
{
	/* padding: 5px */
}
#inner
{
	height: 100%;
	margin: 5px;
}

W zależności od popularności tego tekstu, postaram się opisać więcej błędów i problemów, póki co - czekam na komentarze.

13 » odpowiedzi dla wpisu “Standards versus reality”


  1. 1 zgoda (jarek)

    To było tak "rekapitulując, …". Ale zawsze się przydaje.

    Dzięki. Nie-webmasterom to się na pewno przyda (szczególnie tym, co się brzydzą psiego języka i wszystko chcą po polsku).

  2. 2 Ktoś... | ...tam

    Rewelacyjny artykuł.

    O wielu problemach (nie wspominając o ich rozwiązaniach) dowiedziałem się dopiero z tego tekstu - dzięki wielkie, to szalenie przydatne :)

  3. 3 Patrys

    A najlepiej wsparcie CSS w IE obrazuje arkusz styli Kevina Cornella z bearskinrug.co.uk:

    http://bearskinrug.co.uk/_css/styles.css

  4. 4 Jezuch

    Jakiś czas temu jakimś cudem udało mi się bez podejrzanych sztuczek zrobić menu z zagnieżdżonych ul/li, które wygląda jak należy pod IE i Mozillą :) Oczywiście wkrótce potem dowiedziałem się, że moje arkusze stylów są bardzo nieczytelne, ale podejrzewam że to zawiść ;)

  5. 5 Tomash

    W pytkę. Notki takie jak ta powinny tworzyć odcinek cykliczny, jak najszybciej wydany drukiem :)

  6. 6 Tomash

    A tak swoją drogą, im częściej czytam o standardach, które tak naprawdę nie są standardami (bo "standardowa" przeglądarka ma je w dupie) i kodowych akrobacjach, które mają zapewnić taki sam wygląd strony pod Operą, Mozillami i IE, zastanawiam się, czy by nie pieprznąć w cholerę tego divowego trendu i robić wszystko jak kiedyś, <table>.

  7. 7 ehh

    Przeananlizuje to jak będę w lepszym stanie;p

    Wygląda ciekawie, może rozwiąże u mnie kilka problemów…

  8. 8 MySZ

    odnosnie zagadnien zwiazanych z clear: {both,lef,right}:

    czesto wyjsciem jest tez (w zaleznosci od tego, jak zbudowana jest i ma byc strona) nadanie float'a rodzicowi:

    #rodzic { float: left}

    #rodzic #dziecko {float: left}

    wtedy rodzic, bez dodatkowego taga (np br z clear: both) rowniez sie rozciagnie :)

    przyklad: http://urzenia.net/testy/float.html

  9. 9 bellois

    wiecej, wiecej, wiecej

  10. 10 marmez

    mi się bardo podobał, a dopiero zacząłem w tym grzebać.

    co do pierwszego przykładu, to neistety w FF sie żle wyświetnił (menu bylo nad trescią) w Operze bylo OK, po dorobieniu tego króczka opisanego juz nie nachodziły na siebie. Możliwe ze ja coś pokręciłem. Zresztą nie ważne.

    zgadzam się z przedmówcą.

    więcej wiecej wiecej.

  11. 11 Reinmar

    Genialny artykuł… Który chyba (bo nie mam czasu sprawdzać) rozwiązuje wreszcie mój problem z floatem, na który od kilku miesięcy nie moge znaleść odpowiedzi :)

    Czekam na więcej artykułów!!!!

  12. 12 Elsindel

    Widzisz, Patrys, CSS jest fajne i w ogóle, ale do wersji 2 W3C zapomina w swoim box model o podstawowej prawdziew projektowania graficznego (w tym projektowania stron WWW): potrzebujemy prostokatow w prostokatach, w prostokatach… w dowolnej konfiguracji. Dlatego tabelkami latwiej zrobic pozadany uklad strony niz DIV-ami, chocby nie wiem, jak sie starac. Wystarczyloby, aby W3C dodalo jeden malutki zbior regul CSS pozwalajacy na podzielenie danego kontenera (block-level) na k (k dowolne!) kolumn o dowolnych proporcjach szerokosci. Gdyby to bylo, wszystko byloby cacy… nie czytalem o CSS3, ale podejrzewam, ze nie pojda w te strone, moze znajda jakies inne rozwiazanie…

    Dowod nie wprost, ze uklady kolumnowe (bardziej skomplikowane niz [X|X]) w CSS sa trudne lub nawet bardzo trudne - liczba tutoriali, pokazujacych rozne przemyslne techniki, jak cos osiagnac. I czesto tylko w Firefoksie czy Operze sie da, przyczyna powstania http://glish.com/css/ nie jest slabe wsparcie CSS przez IE.

    Przyczyna jest zle zaprojektowany box model.

    Poza tym jednak kocham CSS i odkad sie pojawilo, pierwsza mysla, gdy chce pisac jakis maly program z jakimkolwiek interfejsem, jest moj firefoks, html, php i css, + ew. javascript. Odechcialo mi sie uczyc Javy lub C# na tyle, by pisac w nich cos z interfejsem. Vivat aplikacje webowe!

    Swoja droga, zastanawiam sie takze, czemu w CSS nie mozna opisywacdlugosci za pomoca kilku jednostek jednoczesnie i (co najmniej) prostych wyrazen arytmetycznych, np.: xxx { width: 10px + 50%}. Byloby to czasami spore ulatwienie, gdy chcemy miec np. marginesy w em/% i tresc w em/%, zas ramki w pikselach (bo border o szerokosci 0.3em na przyklad to dosc niebezpieczna zabawka ;).

  13. 13 Patrys

    Elsindel:

    Tak się składa, że CSS3 ma układ kolumnowy ;].

    Podział na boksy w boksach w boksach to na ogół przyzwyczajenie do myślenia o zamkniętym układzie ze sztywnym podziałem. W CSS masz coś, czego nie dała ci żadna tabelka: pozycjonowanie absolutne i relatywne.

Skomentuj wpis