Estetyczny i użyteczny listing kodu
16 czerwca 2006
Kolejne usprawnienie na blogu - zastanawiam się nad dodaniem gdzieś znaczka Bety - ale tym razem mniej jałowy opis. Przed testowaniem upewnij się, że wczytane masz nowe pliki CSS i JS - Ctrl + Shift + R.

W postach umieszczam często fragmenty kodu HTML / CSS albo JavaScript, dlatego zwróciłem szczególną uwagę na ten element strony. Roger Johansson bodaj pierwszy wpadł na pomysł, aby linijki kodu umieszczać w liście numerowanej. Poddana odpowiedniemu stylowaniu wygląda bardzo profesjonalnie no i oczywiście przejrzyście - porównując ją z tekstem preformatowanym <pre/>.
Do ciekawego stylowania listy jeszcze wrócę, teraz jednak chciałem skupić się na ważnym problemie związanym z użytecznością. Bo o ile <ol/> daje nam automatyczne numerowanie, to kopiowanie takiego kodu nie jest przyjemne. Kopiujemy bowiem także liczby / markery.
Mogłem zawsze wrócić do starego dobrego <pre/>, mogłem korzystając z buga dot. zaznaczania generowanej treści w Firefoksie porobić numerki licznikami (i czekać na klątwę od Quirisa ;-D). Lecz po co, skoro mamy JavaScript.
Skoro pre da się ładnie zaznaczać - niech tak będzie. Podmienimy listę na tekst preformatowany na żywo. Najlepszym sposobem na uporanie się z zadaniem operującym na DOM jest wypisanie sobie w pliku / na kartce punktów kontrolnych. Oto i one:
- Zaaplikować JavaScript wszystkim listingom kodu po załadowaniu się dokumentu.
- Otoczyć listy dodatkowym divem - ułatwienie w relacjach przełącznika z elementami przełączanymi.
- Dodać klikalny przełącznik lista /
<pre/>. - Zamienić każdy element listy na linijkę tekstu aplikując odstępy na podstawie nazw klas.
- Dodać tekst preformatowany przed listę. Ukryć go.
- Ustawić zdarzenie po kliknięciu na przełącznik. Włala.
Zanim przejdziesz dalej możesz chceć zapoznać się z całym kodem JavaScript - korzystałem trochę z Prototype, ale powinno być przejrzyście.
Pierwszy punkt - u mnie są to wszystkie ol.code. Pobieram więc w dokumencie wszystkie elementy ol za pomocą getElementsByTagName a następnie w pętli wykonuję proste sprawdzenie czy aktualna lista ma klasę code. Można zrobić to od razu przez funkcję Prototype getElementsByClassName, ale kiedyś używałem klasy code także dla akapitów.
W moim przypadku należy jeszcze wykonać ponowną konwersję gdy używamy Ajaxa na stronie głównej bloga. Załatwiłem to dodając klasę modified dla zmienionego ol.code i sprawdzając jej obecność w pętli.
Drugi punkt - tworzymy <div/> za pomocą createElement, wstawiamy go przed listę (insertBefore) i na końcu wstawiamy do niego kod za pomocą appendChild. Funkcja insertBefore jest bardzo przydatna i użyjemy jej jeszcze dwa razy - tutaj należy pobrać uchwyt do elementu nadrzędnego (parentNode załatwia sprawę) i wywołać ją na nim z dwoma parametrami - nowym elementem i tym przed którego coś wstawiamy. Przykład (no i upragniona zamiana, niejako demo):
var div = document.createElement("div");ols[i].parentNode.insertBefore(div, ols[i]);div.appendChild(ols[i]);
Trzeci punkt - załatwiłem to spanem. Z lenistwa głównie - stylowanie linków trochę bruździ w kaskadzie, a span jest dry clean jak to się mówi. Leniuchując dalej użyłem innerHTML do wstawienia gotowego wycinka kodu przycisku. Aby ten punkt był zły jak diabli skorzystałem także z wywołania zdarzenia inline = onclick. Mam jednak swoje powody, a zawsze możesz zrobić inaczej niż ja. :P
Czwarty punkt - jako że nadal działamy w pętli (na każdym dostępnym w dokumencie ol.code) wystarczy że pobierzemy wszystkie elementy listy i zrobimy im kuku. Ja przekazałem gotową tablicę do nowej funkcji, żeby było przejrzyściej w kodzie.
var lis = ols[i].getElementsByTagName("li");var output = convListToPre(lis);
W funkcji convListToPre wyszukuję wystąpień klasy space dla elementu listy. Jeśli znajdę - dodaję <br/> więcej. Następnie tak samo szukam klas ix, gdzie x jest numerkiem od 1 do iluś tam (w CSS mam ustawione na razie 5 - więcej wcięć nie potrzebowałem). Jeśli znajdę, to pobieram numerek i następnie w pętli while dodaję tyle podwójnych spacji ile trzeba (i1 = dwie, i3 = sześć). Wreszcie wyciągam kod z elementu i wszystko razem (bo wyniki są agregowane w zmiennej s) wysyłam z powrotem. Chciałem zaznaczyć, że w normalnych przeglądarkach zadziałałoby \n oraz "(spacja)(spacja)" lecz Explorer się foszy i wymaga dopieszczenia.
Pozostaje nudne jak flaki z olejem dodanie <pre/>, w środku niego <code/>, wlanie wyniku konwersji do środka i ukrycie elementu przed wścibskimi oczkami gości.
Na końcu używając funkcji z haniebnego onclick sprawdzamy klasę spana i przełączamy raz to listę uporządkowaną raz to tekst preformatowany. Klasa zmienia także obrazek tła dla przycisku. W celu zaspokojenia zapędów badawczo-poznawczych czytelników dodałem także odpowiedni title. Pre dostał też hacka związanego z overflow. Działa. :)
Wracając do stylowania - takie fajne numerowanie uzyskujemy za pomocą list-style-type: decimal-leading-zero. Szare prostokąty kodu to code zamieniony na element blokowy. Wcięcia to 3ex, 5ex… - ex to szerokość litery x w danym tekście - mamy tutaj stałą szerokość znaku, więc dostajemy równe wcięcie. Odstęp pionowy jest zrobiony klasą space (o czym wcześniej była mowa)
Dopieszczenia wymaga dopełnienie dla listingu na Operze 8.5 przesuwające numerowanie - chociaż nie ustawiałem żadnego paddingu dla li, wygląda na to że jest to mało ważne.
Wpadłem też na pomysł jak pokazać przez CSS że złamano linijkę kodu. Dodałem obrazek tła wypozycjonowany od drugiej linijki w dół. Póki nie można ustawiać wielu obrazków tła na raz pozostaje sklejenie jednego (co wpływa na wygląd gdy zwiększymy rozmiar tekstu, niestety). Efekt:
ol {overflow: hidden;padding-top: 3em; /* 3 razy mnożymy 100% co daje 300% font-size dla elementu li co przekłada się na wysokość tego samego markera */}
Mam nadzieję, że ten artykuł pomoże podjąć właściwe decyzje podczas projektowania swoich stron i umieszczania na nich kawałków kodu. :) Ja tymczasem przymierzam się do czegoś co zautomatyzuje ospanowywanie CSSa w zależności czy mamy właściwość, wartość, selektor i tak dalej - może przydać się w przyszłości.


