Linie poziome - ponownie ładne i estetyczne
02 września 2006
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:
hr {height: 22px;width: 99px;margin: 0 auto;background: url(hr.gif) 0 0 no-repeat;border: none;}
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):
hr {-ms-extension: expression(this.replaceNode(document.createElement("iehr")));}
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.
iehr {display: block;height: 22px;width: 99px;...}
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.
hr::before {content: "***";display: block;text-align: center;}
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:
hr {margin-bottom: 2em;visibility: hidden;}hr::before {content: "***";display: block;text-align: center;color: #AAA;visibility: visible;}
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.
hr {-ms-content: expression(this.parentNode.insertBefore(document.createElement("texthr"), this));}
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.
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:
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ć.
hr {display: none;-ms-content: expression(this.parsed ? 0 : (this.parentNode.insertBefore(document.createElement("texthr"), this), this.parsed = 1));}texthr {display: block;text-align: center;color: #AAA;-ms-content: expression(this.innerHTML = "***");}
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.