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.

Numeracja list uporządkowanych domyślnie jest automatyczna - po wpisaniu znaczników ol i li przeglądarka doda markery w postaci liczb. Jest to zachowanie wymuszone przez style przeglądarki - ustawiony odpowiednio list-style-type (decimal).

Jednak rozpoczęcie liczenia zaczyna się zawsze od 1 i postępuje o tyle samo w górę. Na kartce papieru bez przeszkód mogę zacząć listę od "4.", aby w Wordzie zmienić środkowy numer na 20 wystarczy parę klików. Czy da się to zrobić w HTML?

Do niedawna dało się niesamowicie prosto - wystarczyło zapoznać się z atrybutami start i value. Pierwszy dodany do ol zaczynał liczenie od ustawionej wartości, drugi pozwalał zmienić numer pozycji listy nawet w jej środku.

Ale z różnych powodów (z którymi nie do końca się zgadzam) zostały w ścisłej definicji dokumentu wykreślone. Więc albo używamy nijakiego Transitional albo generujemy błędy składni.

Można rozwiązać problem na dwa sposoby.

Counters

Pierwszy wiąże się z użyciem generowanej zawartości oraz liczników CSS, które odpowiednio ustawione stworzą numerowanie. Wspierają je Opera i Firefox, z czego tylko pierwsza przeglądarka radzi sobie też dobrze z samą GC.

Potrzebujemy zapoznać się z 2 właściwościami: counter-reset, counter-increment oraz funkcją counter(). Wszystkie trzy operują na identyfikatorze licznika, dowolnie nazwanego. W przykładzie użyję "start".

  1. ol {
  2. counter-reset: start;
  3. }
  4. ol li {
  5. list-style-type: none;
  6. counter-increment: start;
  7. }
  8. ol li::before {
  9. content: counter(start) ". ";
  10. }

Co tu się dzieje?

  1. Pierwsza reguła ustawia przez counter-reset licznik start dla listy uporządkowanej ol. Także resetuje go za każdym znalezieniem kolejnego ol.

  2. Druga reguła przez counter-increment zwiększa licznik przy każdym napotkaniu pozycji listy li.

  3. Trzecia reguła wykorzystuje wreszcie GC aby umieścić dane w dokumencie - zmienną licznika (oraz kropkę i spację).

Gdy odpalimy test takiego kodu zobaczymy, że otrzymaliśmy standardowe numerowanie.

Natomiast to nie koniec bojów - numerowanie nie jest ładnie wyrównane, pozycje listy nie są wcięte i wszystko wygląda jakbyśmy dopisali te numery w HTML. W jaki sposób umieścić liczniki jako markery?

Według specyfikacji powinniśmy użyć display: marker, ale wsparcie tej właściwości leży i opala się na Hawajach. Można spróbować użyć display: inline-block, nadać szerokość, mały odstęp i wyrównanie do prawej. Za trudne to jednak dla Firefoksa, a i tak nie jesteśmy w stanie wciąć tekstu listy.

Skorzystałem wreszcie z wyświetlania tabelowego.

  1. ol li {
  2. display: table-row;
  3. }
  4. ol li:before {
  5. display: table-cell;
  6. width: 40px;
  7. text-align: right;
  8. padding-right: 1ex;
  9. }

Test. Tym razem Opera ładnie renderuje listę. Firefox zapomina o licznikach. Normalnie idę się pociąć czerstwą bułką.

Oznacza to mniej więcej, że jakieś standardy istnieją i teoretycznie można zamienić przestarzałe atrybuty na odpowiedniki CSS-owe zachowując odpowiedni układ. Ale nawet najnowsze przeglądarki jeszcze nie są na to gotowe. Pozostaje więc tylko jedno.

JavaScript

Skorzystać ze skryptu, który sprawdzi odpowiednie elementy w poszukiwaniu, dajmy na to, klasy i zaaplikuje atrybuty. Co zyskujemy? Tylko przejście walidacji. Czasem jednak jest to wymagane. Czasami też jesteśmy świadomi, że pozostała część naszego kodu jest tak dobrej jakości, że nie opłaca się schodzić w dół z DOCTYPE.

Napisałem kawałek kodu JS, który szuka w HTML takich konstrukcji:

  1. <ol class="start:100">
  2. <li class="value:1">

I dodaje automatycznie obok odpowiednie atrybuty. Działa także z ujemnymi wartościami. Całość jest bardzo szybka, bo wykonuje się przed window.onload1 i działa na każdej przeglądarce, włącznie z IE.

Powiedziałem każdej? Safari ma jakieś problemy z dodaniem atrybutu start w JS - pomysły? Tak czy inaczej, działające demo.


Zaznaczę jeszcze raz na koniec - nie jestem zwolennikiem obchodzenia w JavaScript błędów walidacji, tutaj jednak wszystkie inne sposoby zawiodły. Dodatkowo klasy w pewien sposób oddają znaczenie elementu - tylko wygląd zmieniamy w JS zamiast w CSS.

  • 1) więcej w następnym wpisie

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.

Informacje o wpisie

Napisał riddle 21 maja 2007 o 00:44

Kategorie: CSS, HTML & Semantyka, JavaScript & DOM

Dodaj do:

Wpisy archiwalne

Archiwum miesięczne

  1. Wojciech Zając 1 21 maja 2007, 02:14

    Safari ma jakieś problemy z dodaniem atrybutu start w JS – pomysły?

    Sprawdzałem w nowym nightly buildzie i działa OK – myślę, że to powinno niedługo być poprawione.

  2. Michał Górny 2 21 maja 2007, 06:52

    Zią, „działające demo” nie działa (tj. serwer leży) q ;.

  3. Konqueror obsługuje counter’y od 3.5.4. Z GC też sobie bardzo dobrze radzi.

  4. E, nie, ten artykuł w ogóle mi się nie podoba. Można by go zatytułować „Jak oszukać Wyrocznię by powiedziała: jest cacy” Równie dobrym rozwiązaniem jest wsadzenie tych atrybutów bezpośrednio w kod HTML, tak czy siak generujemy niezgodne ze standardem drzewo DOM.

  5. Zastanawiam się, czy ten :before dla table-cell jest poprawny… Bo jednak Konqueror tego nie łyka. Zgłosiłem buga KHTML, zobaczymy co powiedzą: http://bugs.kde.org/show_bug.cgi?id=145738

  6. Siergiej: Próbowałem. ;-) A jak piszę, czasem trzeba coś osiągnąć, więc jest na końcu rozwiązanie ostateczne… natomiast nikt nie każe go używać.

  7. Riddle, ale o jakich przypadkach mówisz? Masz na myśli coś w stylu „szef chciał żeby validator się zazielenił, i żeby zaczynała się od pięciu”, czy chodzi o jakieś bardziej semantyczne przypadki na które nie mogę w tej chwili wpaść?

  8. Jeszcze jedno: jak jest z supportem dla counterów w IE6 i 7?

  9. Co ciekawe jak damy kilka li i dla nich display: none; to numerowanie będzie inne (bedą ponumerowane tylko te li które są widoczne). width/height: 0; dla li też nie załatwia sprawy. Ostatecznie można w akcie desperacji zastąpić markery obrazkiem… (dużej desperacji)

  10. Michał Górny 10 21 maja 2007, 17:09

    Siergieju drogi, a mnie przychodzi do głowy taki mały przypadek — jakaś duża lista, podzielona na kilka stron… tylko z drugiej strony, przy takich listach i tak już się pewnie tabelkę zastosuje, a nie zwykłą listę.

  11. Peres, nie zrozumiałeś mnie. Równie dobrze możesz zrobić to samo poprostu wstawiając start="20" do tego drugiego ol a różnica będzie tylko taka, że wyrocznia nie powie cacy.

  12. A ty nie przeczytałeś moich wyjaśniających akapitów. A i wpis nie skupia się na zapodaniu skryptu, tylko na rozwiązaniu zgodnym ze standardami – rozwiązanie niezgodne jest gdyby ktoś bardzo potrzebował tej funkcjonalności.

  13. Poprostu nie spodziewałem się po takim zagorzałym wyznawcy standardów tutoriali jak je omijać. Nigdy wcześniej na tym blogu nie pojawiły się treści typu „Jeśli nie pasują ci divy, to możesz ten layout zrobić na tabelkach”, albo „w ostateczności zastosuj <font />” ...

  14. Trochę sztuka dla sztuki. Jeżeli ktoś będzie bardzo chciał to zastosować dla Strict to sięgnie do start="" i value="" i będzie mu ganz egal czy to jest zgodne czy nie.

    Co nie znaczy, że rozwiązanie nie jest dobre. Jest słuszne, jednak jak mówię, tylko dla prawdziwych maniaków-standardistów :)

  15. Maniaków Validacji, nie standardów…

  16. Winhelp 16 21 maja 2007, 22:24

    Nasuwa się pytanie, po kiego grzyba zaczynać numerację od 5 i gdzieś w środku przeskoczyć kilka pozycji ;)

  17. To się przydaje, kiedyś była mi potrzebna lista zaczynająca się od zera… albo tak jak peres pisał – kilka kolumn

  18. Ja bym zwyczajnie olał zielenienie się walidatora – ściachanie tych atrybutów ze Stricta jest IMHO absurdalne i pozbawione sensu. Gorzej, jeżeli klient jako wyrocznię w sprawie jakości strony traktuje właśnie walidator ;P

  19. Ściąganie tego ze stricta nie było bez sensu, numerowanie to prezentacja, dlatego powinien tym się zajmować css. A klient i wyrocznia – to jedyne zastosowanie dla tego skryptu jakie jestem w stanie sobie wyobrazić

  20. Siergiej, zastanów się po co te komentarze skoro napisałem to dwa razy we wpisie. :-) Nie chcę być nieuprzejmy, ale się powtarzasz.

  21. Tak, wiem:) Po prostu chcę jasno i wyraźnie podkreślić że to bardzo złe podejście i jestem takowemu mocno przeciwny

  22. Zawsze się przyda, dzięki :)

  23. Siergiej: tak, wiem, ze to prezentacja – i wywalenie tego ze stricta miało swoje cele. Ale jakby nie patrzeć, wywalenie nie pociągnęło za sobą ładnego CSSowego substytutu. Countery to armata na komara, a reszta we wpisie Riddle’a to działający, acz jednak workaround. To jest czasem denerwujące, że niektóre rzeczy dla strikta trzeba strasznie obchodzić, bo nie da się prosto. IMHO.

  24. Mateusz Papiernik: mniej więcej tak samo napisał autor quirksmode.org na temat Strict Doctype‘ów i najzwyczajniej do swoich stron nie wsadza żadnego doctype’a ;)

  25. ppk nie używa DOCTYPE-ów? Chyba na najstarszych stronach. :-) On natomiast umieszcza komentarze na dole strony czemu się nie waliduje, jeśli tak się zdarzy.

  26. Według mnie porządek elementów w liście jest zdecydowanie w gestii struktury a nie prezentacji – poprzez porządkowanie wartościujemy elementy np. pierwszy jest najważniejszy, drugi jest mniej ważny.. pierwszy jest najcięższy drugi lżejszy itp.. a to przecież sprowadza się do znaczenia zawartości elementu listy.
    Jeśli nie mamy dostępnych dedykowanych atrybutów do zarządzania tym porządkiem to chyba tak jak w przypadku innych braków najlepsza droga to skorzystanie z neutralnych elementów jak div lub span i wklepanie „numerków” tekstowo.

    Numerowanie przez css stwarza kolejny problem – jak będzie wyglądać nie ostylowany dokument z listą uporządkowaną zaczynającą się od 100-stu i stopniowaną co dwa? – nastąpi przekłamanie, bo przeglądarka stosując swój domyślny styl zacznie numeracje od 1-ki :)
    Myślę, że da się również odnaleźć czysto prezentacyjne zastosowanie numeracji elementów i pewnie do takich zastosowań zostały opracowane właściwości z rodziny counter.

    Jest szansa (choć nie wiem na ile aktualna ;) , że atrybuty dedykowane porządkowaniu listy wrócą do rekomendacji html’a (wersja 5) przynajmniej osoba, która w tym palce macza swego czasu też wyraziła zdanie, że atrybuty ‘start’ i ‘value’ nie dotyczyły warstwy prezentacji i ta funkcjonalność powinna wrócić: http://annevankesteren.nl/2004/03/ordered-lists-start-and-value-attributes

  27. Konrad Tarantowicz 27 25 maja 2007, 20:57

    być może cos pominąłem lub czegoś niedoczytałem…, w poście skupiasz się jednak na nowoczesnych przeglądarkach i pomijając JavaScript też przy tym pozostane
    ol { counter-reset: licz +5; }
    ol li { display: block; margin: 10px 0 0; padding: 0; width: 98%; }
    ol li:before { content: counter(licz, decimal) "."; counter-increment: licz; margin:0 0 -20px -25px; display: block; width: 20px; height: 20px; }
    /* ol li:before { counter-increment: licz +2; } */

    i ponoć są koderzy, którzy nie lubią ujemnych wartości, to jeżeli lepiej zapisać, niż tylko na kolanie, wartości modelu pudełka (em/ex?) to moim zdaniem jest całkiem zadowalające

    poza tym nie bardzo rozumiem dlaczego ktoś miałby ustawiać pierwszy element na inny niż 1? – najprostsza odpowiedź że kontynuowanie tej samej listy logicznej w kilku różnych odseparowanych od siebie listach. To jednak daje możliwość tak czy siak odwołania sie do kolejnej list chocby po id, jest jeszcze kilka innych wariacji, ale jedyna przychodząca mi do głowy odpowiedź to wylistowanie konkretnych pozycji z pierwotnej listy z uwzględnieniem oryginalnych pozycji np. lista 10 warunków, 5 losowych warunków nie zostało spełnionych, warunku 1, 4, 6 7 i 9 nalezy wylistować

    z :nth-child byłoby jeszcze prościej

  28. Ten podwójny dwukropek (::) przy pseudoelementach (np. ::before, ::after) to dopiero rekomendacja CSS 3. CSS 2.1 ma pojedyńcze zarówno dla pseudoklas, jak i pseudoelementów. Działa to oczywiście wszędzie gdzie ma działać. Masz jakąś jasno zdefiniowaną politykę w zakresie „progressive enhancement a obecny standard”?

    Sorry za zboczenie z tematu.

  29. IE7 zaczyna numerować w tym przykładzie od 100 ;]
    Pomijając fakt, że IE i takie bajery w CSS to chyba już tylko marzenia…

  30. Przepraszam, mój błąd – ScriptBlock, działa ok