Budowa formularzy z wykorzystaniem standardów
06 marca 2006
Standardy sieciowe i przejrzysty kod czasem stwarzają problemy z rzeczami bardzo prostymi. Jeden z nich jest dość pospolity - musimy złożyć formularz. W większości przypadków nie powinniśmy korzystać z tabeli - formularz napisany na blokach div bądź im podobnych elementach może być później przekształcony, tabela jest statyczna i mało efektywna.
Na początek napiszmy strukturę takiego formularza. Wykorzystamy znaczniki, które przez co niektórych webmasterów zostały już zapomniane, bądź nigdy nie używane. Fieldset jest zestawem pól formularza, w naszym będzie tylko jeden taki zestaw.
<form action=""><fieldset></fieldset></form>
Fieldset posiada domyślne dopełnienie w środku, odsuwające kontrolki od, także domyślnego, obramowania. Można wyłączyć taki wygląd przez CSS.
fieldset {border: none;padding: 0;}
Następnie możemy nazwać nasz zestaw pól, bądź w tym wypadku, formularz. Służy do tego znacznik legend. Umiejscawia się domyślnie centralnie na krawędzi fieldset, gdybyśmy nie wyłączali obramowania byłoby to dobrze widoczne.
<form action=""><fieldset><legend>Komentarz</legend></fieldset></form>
Dalej wypada zacząć dodawać pola formularza. Input, textarea, select gdy potrzeba. W przypadku elementów input warto nadać klasę określającą z jakim typem mamy do czynienia.
<input class="text" type="text" /><input class="check" type="checkbox" /><input class="radio" type="radio" />
Bo o ile możemy korzystając z selektorów atrybutów odnieść się do każdego z typu przez:
input[type=text], input[type=radio]…
To starsze przeglądarki oraz Internet Explorer 6 nie zrozumieją takiego zapisu. Powinno się oddzielić style dla pól formularza, bo nie wszystkie właściwości CSS aplikują się jednakowo we wszystkich agentach. Osobiście jestem także zwolennikiem niestylowania przycisków submit - zwykle to przeglądarka korzystając z wyglądu kontrolek systemowych powinna się nimi zająć. O ile jeszcze nadanie stylu „przycisku” - wypukły kawałek zakolorowanego miejsca mówi, że da się go kliknąć, to zupełnie płaski, niczym tekstowy input jest złym rozwiązaniem.
Kolejnym elementem wartym wspomnienia, co więcej - prawie niezbędnym w przypadku pól checkbox czy radio - jest label. Etykietka tekstowa przypisana do którejś z kontrolek, aktywująca focus bądź zaznaczająca pole wyboru w tej kontrolce. Koniec z kliknaniem maleńkiego kwadracika żeby zaznaczyć pole - kliknij jego opis. Tak więc wygląda nasz formularz:
<form action=""><fieldset><legend>Komentarz</legend><label for="fNick">Nick</label><input id="fNick" name="fNick" class="text" type="text" /><label for="fMail">Mail</label><input id="fMail" name="fMail" class="text" type="text" /><label for="fText">Treść</label><textarea id="fText" name="fText" cols="40" rows="10"></textarea><input id="fSave" name="fSave" class="check" type="checkbox" /><label for="fSave">Zapamiętaj dane</label><input id="fSubmit" name="fSubmit" class="submit" type="submit" value="Dodaj komentarz" /></fieldset></form>
Uwaga do zapamiętania, jeśli jeszcze ktoś nie kojarzy - o ile w XHTML nie identyfikuje się już elementów za pomocą name, tylko id, to pola formularzy korzystają z atrybutu name aby przekazać skryptowi po stronie serwera wymagane zmienne i dane.
Zarówno input (textarea, nieużyty tutaj select tak samo) jak i label są elementami liniowymi, co oznacza że pojawiają się na stronie jak tekst - jeden za drugim ciurkiem. Dlatego osoby początkujące starające się podejść drogą standardów do formularzy często wstawiają znacznik złamania linii za każdym inputem - br.
Rozwiązanie to nie jest optymalne. Wyglądać może i będzie znośnie, ale nie na samym wyglądzie się teraz skupiamy. Ja proponowałbym ując każdą kontrolkę z jej opisem w div. Div jako element blokowy łamie linię przed sobą i za sobą co stworzy podobny efekt rozdzielenia pól formularzy; także gdy wyłączymy style. Co więcej - gdyby nam kiedyś znudził się wygląd takiego formularza, możemy zmienić element div w form na liniowy. Podejście metodą br wymagałoby usunięcia tych znaczników z wielu stron.
<form action=""><fieldset><legend>Komentarz</legend><div><label for="fNick">Nick</label><input id="fNick" name="fNick" class="text" type="text" /></div><div><label for="fMail">Mail</label><input id="fMail" name="fMail" class="text" type="text" /></div><div><label for="fText">Treść</label><textarea id="fText" name="fText" cols="40" rows="10"></textarea></div><div><input id="fSave" name="fSave" class="check" type="checkbox" /><label for="fSave">Zapamiętaj dane</label></div><div><input id="fSubmit" name="fSubmit" class="submit" type="submit" value="Dodaj komentarz" /></div></fieldset></form>
Po lekkim (tylko wygląd kontrolek, kolory oraz tekst) ostylowaniu otrzymujemy taki oto formularz. Pierwszą rzeczą rzucającą się w oczy jest brak wyrównania pól tekstowych względem siebie - sterczą nierówno za tekstem.
Należy je odepchnąć równo od opisów pól, aby stworzyły siatkę jak w przypadku tabeli. W tym przykładzie zamienimy w CSS label na element blokowy i nadamy mu szerokość.
label {display: block;width: 100px;}
Zamiast pikseli można także użyć procentów, emów, etc. Efekt jaki osiągneliśmy nadal nie jest taki jakbyśmy sobie życzyli. Label z uwagi na blokowość wytworzył nową linię za sobą i przeniósł input pod siebie. Więc należy zamienić input na blok oraz ustawić pływanie w lewą stronę, tak samo jak w przypadku label.
label {display: block;width: 100px;float: left;}input, textarea {display: block;float: left;}
Po włączeniu float należy zadbać, aby poszczególne linijki pól formularza - złożone z divów - nadal rozdzielały kontrolki. Jako że wszystkie elementy w środku diva zostały wybite z flowu dokumentu, więc on sam stanie się prawie bezużyteczny - pusty div nie przedstawia wartości na stronach. Należy więc zadbać, aby zaczął się „przejmować” elementami które są w środku, nawet jeśli mają float i wystają. Możemy także dodać od razu odstęp pionowy.
div {overflow: hidden;clear: both;margin-bottom: 0.5em;}
Dodałem także clear: both, aby upewnić się że na pewno formularz pojawi się tak jak sobie wymarzyłem (Internet Explorer zwykle w tym miejscu się foszy). Efekt jest już lepszy. Pozostał tylko ten checkbox, który ze względu na odwrotne położenie elementów label i input w kodzie został z lewej strony bez odstępu. Można to zmienić aplikując input z klasą check lewy margines:
input.check {margin-left: 100px;}
Oraz wyłączyć szerokość dla etykietki tego pola, której dodajemy też klasę:
label.check {width: auto;}
Podobnie robimy z samotnym przyciskiem wysyłającym komentarz:
input.check,input.submit {margin-left: 100px;}
Gotowy formularz prezentuje się całkiem jak tabela - jest jednak wolny od niesemantycznych znaczników i działa sprawniej za pomocą label - kliknij w tekst etykietki formularza a zobaczysz jak pozytywny efekt powoduje.
Z powodu błędu obliczania odległości po zaaplikowaniu float dla elementów, w Internet Explorerze checkbox oraz przycisk będą odsunięte o 100px za bardzo w prawo. Można rozwiązać to stosując komentarz warunkowy z dodatkową regułą CSS naprawiającą błąd:
<!--[if lte IE 6]><style type="text/css">input.check,input.submit {display: inline;}</style><![endif]-->
Nie jest to jednak jedyny elegancki sposób umieszczania kontrolek w formularzu. Od jakiegoś czasu jestem zwolennikiem używania listy definicji dl wraz ze znacznikami dt i dd. Zastanawiałem się czy można nagiąć nazwę „lista definicji” aby wytłumaczyć to przeznaczenie - lecz jeśli przyjąć, że tytułem dt jest etykietką pola a definicją dd samo pole, to chyba nic złego się nie stanie.
Niewątpliwym plusem takiego rozwiązania jest bardzo przejrzysty układ widoczny po wyłączeniu stylów. Tak to zrobione zostało między innymi w panelu administracyjnym tego bloga. W niedługim czasie mam zamiar opisać więcej sposobów na radzenie sobie z formularzami i ich wyglądem, tam też znajdzie się tutorial struktury opartej na dl.