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.

Linię poziomą hr wyrzucono z obiegu, twierdząc że jest znacznikiem prezentacyjnym i może być z powodzeniem zastąpiona przez odpowiednie bloki grupujące resztę elementów na stronie. Chciałbym jednak wlać w nią trochę życia, przynajmniej zanim zostanie skreślona ze specyfikacji. Pokażę w jaki sposób można ładnie ostylować te elementy - tak aby efekt był identyczny w większości popularnych przeglądarek.

Separator graficzny

Ładny efekt uzyskuje się zamieniając linię poziomą na separator graficzny. W tym celu należy wyłączyć obramowanie, nadać wymiary równe wymiarom obrazka i ustawić go jako tło. Na końcu można ładnie wyśrodkować za pomocą automatycznych marginesów:

  1. hr {
  2. height: 22px;
  3. width: 99px;
  4. margin: 0 auto;
  5. background: url(hr.gif) 0 0 no-repeat;
  6. border: none;
  7. }

Wszystko pięknie, ale Explorer (zarówno 6 i 7) nie respektuje takiego zapisu i wyświetla obramowanie (screen). Przeszukałem sieć, próbowałem z własnymi hackami - marginesy ujemne, overflow - nic. Wszędzie radzą obłożyć hr-kę divem z klasą i ukrywając linię aplikować CSS dla diva.

No dobra, to też jest jakieś wyjście, ale skoro problemem tak naprawdę jest tylko renderowanie elementu hr to trzeba się go pozbyć i to bez dodawania nowych znaczników. Z pomocą przychodzą expressions.

Zamieniłem hr na wzięty z kosmosu element iehr. W arkuszu dla IE7 i niższych (if lte IE 7):

  1. hr {
  2. -ms-extension: expression(this.replaceNode(document.createElement("iehr")));
  3. }

Następnie pozostało przypisać w tym samym arkuszu właściwości CSS dla iehr. Takie same jak dla hr wyżej, należy tylko dodać wyświetlanie blokowe.

  1. iehr {
  2. display: block;
  3. height: 22px;
  4. width: 99px;
  5. ...
  6. }

No i działa - obramowań nie ma. :-)

Separator tekstowy

Kolejny efekt jaki można uzyskać za pomocą linii poziomej jest często widywany w gazetach albo książkach. Mowa o rozdzielaniu bloków tekstu trzema gwiazdkami. O ile można po prostu użyć obrazka tła z narysowanymi "***", to użycie zwykłego tekstu pozwala skalować oraz kopiować taki separator.

I w tym wypadku będziemy musieli poczarować dla IE, ale zanim to nastąpi proponuję rozwiązanie bardzo eleganckie, skorzystanie z generowanej treści.

  1. hr::before {
  2. content: "***";
  3. display: block;
  4. text-align: center;
  5. }

W ten sposób dostaniemy koło linii nasze wyśrodkowane trzy gwiazdki. Należy teraz zająć się jeszcze samą linią. Nie chcemy jej oglądać, ale zgodnie z zasadą o wyświetlaniu ramek wokoło elementów, nie możemy jej ukryć przez display: none a generowaną treść przywrócić przez block.

Pozostaje visibility ustawione na hidden, a dla hr::before visible. Należy jeszcze dograć margines i efekt jest doskonały:

  1. hr {
  2. margin-bottom: 2em;
  3. visibility: hidden;
  4. }
  5. hr::before {
  6. content: "***";
  7. display: block;
  8. text-align: center;
  9. color: #AAA;
  10. visibility: visible;
  11. }

Błąd jest jeden - Firefox nie pozwala nadal zaznaczać tekstu generowanego przez CSS, w odróżnieniu od Opery. Nie będę jednak z tym walczył wyczyniając cuda na kiju przez XBL i rezygnując z poprawnych stylów - haki zostawiamy tylko dla IE, a w Fx niech wypuszczą w końcu do tego patcha.

Czas przejść do meritum. Musimy wygenerować tekst przed hr oraz nadać mu odpowiednie style. O ile CSS pozwala zamienić tekst na element blokowy i nadać mu żądane reguły, tak metoda insertAdjacentText - której zwykle używam do uzyskiwania przed / po separatorów albo punktorów - nie sprawdzi się.

Po prostu wygeneruję, podobnie jak opisałem w przykładzie wyżej, swój element przed hr i odpowiednio go ostyluję. Skorzystałem w tym przypadku z metody insertBefore.

  1. hr {
  2. -ms-content: expression(this.parentNode.insertBefore(document.createElement("texthr"), this));
  3. }

Zanim wkleisz taki kod do swojego dokumentu, zatrzymaj się na chwilę. Pomyślmy o tym co by się stało, gdyby uruchomić stronę z takim expression. Po każdym zdarzeniu oraz zmianach rozmiarów okna CSS jest jeszcze raz parsowany i aplikowane są właściwości, gdyby było to potrzebne.

Taki kod spowoduje zawieszenie się przeglądarki, ponieważ Explorer będzie wstawiał nieskończenie wiele nowych elementów texthr przed każdą linią poziomą. Należy więc zapobiec temu zachowaniu.

  1. this.parsed ? 0 : (……, this.parsed = 1)

Taki kod spowoduje, że każdy hr przed który wstawiono element (tutaj wykropkowano metodę za to odpowiedzialną) dostanie właściwość parsed ustawioną na 1. Przy ponownym parsowaniu nie wywoła jeszcze raz wstawienia elementu, ponieważ ustawiłem warunek:

  1. Jakiś warunek ? Czy zgodne z wartością : Jeśli nie zgodne to…

To taki skrócony if / else.

Mamy teraz kompletne expression, więc wystarczy ukryć linię poziomą oraz wstawić do nowego elementu tekst i odpowiednio go ostylować.

  1. hr {
  2. display: none;
  3. -ms-content: expression(this.parsed ? 0 : (this.parentNode.insertBefore(document.createElement("texthr"), this), this.parsed = 1));
  4. }
  5. texthr {
  6. display: block;
  7. text-align: center;
  8. color: #AAA;
  9. -ms-content: expression(this.innerHTML = "***");
  10. }

Na stronie testowej przedstawiłem efekt działania takiego kodu. Mam nadzieję, że się podobało - ja z dnia na dzień coraz bardziej lubię expressions. :)


Eshaw znalazł lepszy i krótszy sposób na usunięcie obramowania z hr.

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 02 września 2006 o 17:48

Kategorie: CSS, Design, Internet Explorer

Dodaj do:

Wpisy archiwalne

Archiwum miesięczne

  1. Dzięki, tego mi było trzeba. Swoją drogą gdzie można się więcej dowiedzieć o wspomnianych "expressions" i co to wogóle jest?

  2. Hmm… no właśnie nie można, albo inaczej - nie znajdziesz kompendium wiedzy o tym czymś. Ja szukam, szperam i czasem znajduję coś pożytecznego, ale większość piszę sam, wszakże to JavaScript tylko z paroma ograniczeniami + możliwościami, bo to IE jego czasem pożyteczne dziwactwa. ;]

  3. ktoregos razu bawilem sie hr'em i z tego co pamietam udalo mi sie bez czasow zlikwidowac obramowanie pod IE, ale szczerze powiedziawszy nie pamietam jak i gdzie..
    jak sobie przypomne albo znajde chwile i sprobuje znowu, to dam znac

  4. shw: color: #FFF; na przykład, ale jak ustawisz obrazek tła na hr to wtedy on znika przykryty tym kolorem.

  5. No kompedium może nie ma, ale w msdn'ie może poczytać o tego typu rozwiązaniach.
    http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/overview/recalc.asp

  6. Ale hr przecież znika ze specyfikacji, zgodnie z pierwszym akapiem tej notki. Po co więc z nią kombinować? Ja chętniej poczytałbym czym i jak ją zastąpić wg. nowej specyfikacji, jakkolwiek ona się będzie zwać.

  7. W XHTML2 będzie prawdopodobnie strukturalny element o niespodziewanej nazwie... <separator/>

    http://www.w3.org/TR/xhtml2/mod-structural.html#edef_structural_separator

    Czyli ma on w założeniu oddzielać od siebie części dokumentu, ale bez wizualnego zadeklarowania jak w przypadku <hr/>.

  8. Przecież to tylko przeglądarki robią z tego linię, a do tego można z wizualizacji odstępu nie skorzystać - wyłączyć via CSS. Uważam przez to pomysł z nowym elementem o nowej nazwie ale starym wszystkim innym (z tego co podlinkowałeś tak wynika, nawet trzeba go umieszczać w środku li, bezsens)… jest dziwny i niepotrzebny.

  9. Świetny pomysł. Sam od kąd tylko zacząłem zabawę z CSS wykorzystywałem standardowe znaczniki, w tym hr - dokładnie tak jak to opisałeś na początku (jako element graficzny).

  10. rzeczywiscie - w tym co robilem uzylem color: _kolor_tla_

    ale jest jeszcze inny sposob - dodanie tych linijek specialnie dla IE powinno zalatwic sprawe:

    filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=hr.gif)
    progid:DXImageTransform.Microsoft.Matrix(Dx=-1);
    height: 0px;
    width: 0px;

  11. Docelowo <separator /> wcale nie bedzie musiał być poziomy, a tymbardziej być kreską. P.S ciekawy tekst i dużo poświęconego czasu jak na taki gówniany element jak <hr />. Dzięki

  12. Nie wiem czy jest sens pisać aż taka ilość kodu, jak można dużo łatwiej:

    http://turnipspatch.com/test/blog-posts/hr-background-image/

    Ps: czy tutaj pisze coś w stylu "komentarz został dodany do kolejki oczekujących"?

  13. Paweł `hwao` Halicki: Okej, fajnie, że można prociej. :] Mój przykład natomiast jest bardziej kompleksowy i pozwala wycisnąć więcej z hr.

    Tu nie ma kolejki moderacyjnej w ogóle, a ja nic nie skasowałem.

  14. Jest błąd na stronce.
    Mianowicie w prawym menu na samej górze jest część kodu niewykonywana:

    this.nodeValue = 'hello'

    :)

  15. Haha, ile jeszcze ludzi wytknie tą dekorację jako błąd? :D

  16. może dodaj przy dekoracji title="to nie jest błąd" ?

  17. Zaciekawiłeś mnie tymi atrybutami '-ms-extension' i '-ms-content' czy można w ogóle jakieś specyfikacje do nich znaleźć? Przeszukuje msdn i udało mi się znaleźć tylko wzmiankę o atrybucie '-ms-interpolation-mode'.
    Skąd żeś je wytrzasnął? :)

  18. Możesz tak samo wpisać -nie-lubie-kaczynskiego: expression(); :) To po prostu sposób na przekazanie kodu do strony.

  19. "Jakiś warunek ? Czy zgodne z wartością : Jeśli nie zgodne to…"

    Hmm, raczej miało być:
    Jakiś warunek ? Jeśli zgodne z wartością : Jeśli nie zgodne to...

  20. Wie ktoś może, jak zlikwidować (albo chociaż kontrolować) marginesy pionowe w IE? Na Fx'ie i Operze zwyczajne margin działa świetnie, niestety w IE nie :/

    Btw: jeśli ktoś chce po prostu zrobić sobie jednokolorową linię, to należy ustawić jednocześnie background-color i border: none (dla normalnych broweserów) oraz color (dla IE). Potem jej grubością można manipulować via height.

    Do szczęścia brakuje mi tylko zlikwidowania marginesów w pionie ...

  21. Marginesy w moim przykładzie pochodzą od akapitu p.

  22. Hmmm... http://img90.imageshack.us/my.php?image=hrsu0.png
    Win XP SP2 Eng, IE 6.0

  23. Czytanie ze zrozumieniem wychodzi… JavaScript włącz, meeen. ;]

  24. Informacyjnie: ten „skrócony if / else” nazywa się /operator wyrażenia warunkowego/

  25. przydatny tutorial. tx

  26. „Linię poziomą hr wyrzucono z obiegu, twierdząc że jest znacznikiem prezentacyjnym”

    ...i tak to za jakieś 10 lat pozostanie nam już tylko <span> ;)

  27. Przed chwila zastosowałem w praktyce (hr->iehr), działa na 100% tak jak trzeba, wielkie dzięki! :) Przydało się

  28. @smk: A kto był Twoim tłumaczem? Z jakich polskojęzycznych książek wziąłeś „operator wyrażenia warunkowego”? Ja się spotkałem tylko z nazwą „ternary if”. A dociekając istoty, to ten przedmiot myśli powinien się raczej nazywać całym wyrażeniem warunkowym, a nie tylko operatorem ( ? : ). Gdybym ja redagował podobny tekst programistyczny, przetłumaczyłbym to raczej podobnie do riddle’a.

    Podaj, na jaki autorytet się powołujesz mówiąc „tak się to nazywa”. Dla mnie osobiście większym autorytetem jest autor tego bloga niż książki Helionu.