Idealny spis treści z HTML + CSS PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Doskonały spis treści z HTML + CSS

Na początku tego roku samodzielnie opublikowałem ebooka zatytułowanego Zrozumienie obietnic JavaScript (za darmo do pobrania). Mimo że nie miałem zamiaru przekształcić go w książkę drukowaną, wystarczająco dużo osób zwróciło się z zapytaniem o wersję drukowaną, którą postanowiłem opublikować samodzielnie. Pomyślałem, że będzie to łatwe ćwiczenie z wykorzystaniem HTML i CSS do wygeneruj plik PDF, a następnie wyślij go do drukarki. Nie zdawałem sobie sprawy, że nie mam odpowiedzi na ważną część książki drukowanej: spis treści.

Makijaż spisu treści

W istocie spis treści jest dość prosty. Każdy wiersz reprezentuje część książki lub strony internetowej i wskazuje, gdzie można znaleźć tę treść. Zazwyczaj wiersze składają się z trzech części:

  1. Tytuł rozdziału lub sekcji
  2. Linie wiodące (tj. kropki, kreski lub linie), które wizualnie łączą tytuł z numerem strony
  3. Numer strony

Spis treści można łatwo wygenerować w narzędziach do przetwarzania tekstu, takich jak Microsoft Word lub Google Docs, ale ponieważ moja treść była w Markdown, a następnie została przekształcona w HTML, nie była to dla mnie dobra opcja. Chciałem czegoś zautomatyzowanego, który współpracowałby z HTML w celu wygenerowania spisu treści w formacie nadającym się do druku. Chciałem również, aby każda linia była linkiem, aby można było jej używać na stronach internetowych i w plikach PDF do poruszania się po dokumencie. Chciałem również, aby kropki między tytułem a numerem strony były liderami.

I tak zacząłem badania.

Natknąłem się na dwa doskonałe posty na blogu dotyczące tworzenia spisu treści za pomocą HTML i CSS. Pierwszym był „Zbuduj spis treści ze swojego kodu HTML” przez Julie Blanc. Julie pracowała PagedJS, wypełnienie dla brakujących funkcji multimediów stronicowanych w przeglądarkach internetowych, które prawidłowo formatuje dokumenty do druku. Zacząłem od przykładu Julie, ale stwierdziłem, że nie do końca mi to pasuje. Następnie znalazłem Christopha Grabo „Responsywne linie lidera TOC z CSS” post, który wprowadził koncepcję użycia CSS Grid (w przeciwieństwie do podejścia Julie opartego na liczbach zmiennoprzecinkowych), aby ułatwić wyrównanie. Jednak po raz kolejny jego podejście nie było do końca odpowiednie dla moich celów.

Jednak po przeczytaniu tych dwóch postów poczułem, że mam wystarczająco dobre zrozumienie problemów z układem, aby rozpocząć samodzielne. Wykorzystałem fragmenty z obu postów na blogu, a także dodałem kilka nowych koncepcji HTML i CSS do podejścia, aby uzyskać wynik, z którego jestem zadowolony.

Wybór właściwego znacznika

Decydując się na poprawny znacznik dla spisu treści, myślałem przede wszystkim o poprawnej semantyce. Zasadniczo spis treści dotyczy powiązania tytułu (rozdziału lub podsekcji) z numerem strony, prawie jak para klucz-wartość. To doprowadziło mnie do dwóch opcji:

  • Jedną z opcji jest użycie tabeli (<table>) z jedną kolumną na tytuł i jedną na stronę.
  • Jest też często nieużywana i zapomniana lista definicji (<dl>) element. Działa również jako mapa klucz-wartość. Tak więc po raz kolejny związek między tytułem a numerem strony byłby oczywisty.

Każda z tych opcji wydawała się dobrą opcją, dopóki nie zdałem sobie sprawy, że tak naprawdę działają tylko w przypadku jednopoziomowych spisów treści, a mianowicie tylko wtedy, gdy chcę mieć spis treści zawierający tylko nazwy rozdziałów. Jeśli jednak chciałem pokazać podrozdziały w spisie treści, nie miałem dobrych opcji. Elementy tabeli nie nadają się do danych hierarchicznychi chociaż listy definicji mogą być technicznie zagnieżdżone, semantyka nie wydaje się poprawna. Więc wróciłem do deski kreślarskiej.

Postanowiłem wykorzystać podejście Julie i użyć listy; jednak zdecydowałem się na uporządkowaną listę (<ol>) zamiast listy nieuporządkowanej (<ul>). Myślę, że uporządkowana lista jest w tym przypadku bardziej odpowiednia. Spis treści przedstawia listę rozdziałów i podtytułów w kolejności, w jakiej pojawiają się w treści. Kolejność ma znaczenie i nie powinna zgubić się w znacznikach.

Niestety, użycie uporządkowanej listy oznacza utratę związku semantycznego między tytułem a numerem strony, więc następnym krokiem było ponowne ustanowienie tej relacji w każdym elemencie listy. Najprostszym sposobem rozwiązania tego problemu jest po prostu wstawienie słowa „strona” przed numerem strony. W ten sposób stosunek liczby do tekstu jest jasny, nawet bez żadnego innego wizualnego rozróżnienia.

Oto prosty szkielet HTML, który stanowił podstawę moich znaczników:

<ol class="toc-list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title</span> <span class="page">Page 1</span> </a> <ol> <!-- subsection items --> </ol> </li>
</ol>

Stosowanie stylów do spisu treści

Po ustaleniu znaczników, których zamierzałem użyć, następnym krokiem było zastosowanie kilku stylów.

Najpierw usunąłem automatycznie generowane liczby. Jeśli chcesz, możesz zachować numery generowane automatycznie we własnym projekcie, ale często zdarza się, że książki mają nienumerowane przedmowy i posłowia zawarte na liście rozdziałów, co sprawia, że ​​numery generowane automatycznie są nieprawidłowe.

W moim celu ręcznie wpisałbym numery rozdziałów, a następnie dostosowałbym układ tak, aby lista najwyższego poziomu nie miała żadnego wypełnienia (w ten sposób wyrównując ją z akapitami), a każda osadzona lista była wcięta o dwie spacje. Zdecydowałem się użyć 2ch wartość dopełnienia, ponieważ nadal nie byłem pewien, której czcionki użyć. The ch jednostka długości pozwala, aby dopełnienie było zależne od szerokości znaku — bez względu na użytą czcionkę — zamiast bezwzględnego rozmiaru w pikselach, który mógłby wyglądać niespójnie.

Oto CSS, na którym skończyłem:

.toc-list, .toc-list ol { list-style-type: none;
} .toc-list { padding: 0;
} .toc-list ol { padding-inline-start: 2ch;
}

Sara Sueidan zwrócił mi uwagę, że przeglądarki WebKit usuwają semantykę listy, gdy list-style-type is none, więc musiałem dodać role="list" do kodu HTML, aby go zachować:

<ol class="toc-list" role="list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title</span> <span class="page">Page 1</span> </a> <ol role="list"> <!-- subsection items --> </ol> </li>
</ol>
Kod zastępczy do osadzenia w CodePen

Stylizacja tytułu i numeru strony

Z listą stylizowaną zgodnie z moimi upodobaniami, nadszedł czas, aby przejść do stylizacji pojedynczego elementu listy. W przypadku każdej pozycji w spisie treści tytuł i numer strony muszą znajdować się w tym samym wierszu, z tytułem po lewej stronie i numerem strony wyrównanym do prawej.

Możesz pomyśleć: „Nie ma problemu, po to jest flexbox!” Nie mylisz się! Flexbox rzeczywiście może osiągnąć prawidłowe wyrównanie strony tytułowej. Ale są pewne trudne problemy z wyrównaniem przy dodawaniu liderów, więc zamiast tego zdecydowałem się na podejście Christopha za pomocą siatki, co jako bonus, ponieważ pomaga również w przypadku tytułów wielowierszowych. Oto CSS dla pojedynczego elementu:

.toc-list li > a { text-decoration: none; display: grid; grid-template-columns: auto max-content; align-items: end;
} .toc-list li > a > .page { text-align: right;
}

Siatka ma dwie kolumny, z których pierwsza to auto-rozmiar, aby wypełnić całą szerokość kontenera, pomniejszoną o drugą kolumnę, która ma rozmiar max-content. Numer strony jest wyrównany do prawej strony, zgodnie z tradycją w spisie treści.

Jedyną inną zmianą, jaką wprowadziłem w tym momencie, było ukrycie tekstu „Strona”. Jest to przydatne dla czytników ekranu, ale niepotrzebne wizualnie, więc użyłem tradycyjny visually-hidden klasa aby ukryć to przed widokiem:

.visually-hidden { clip: rect(0 0 0 0); clip-path: inset(100%); height: 1px; overflow: hidden; position: absolute; width: 1px; white-space: nowrap;
}

I oczywiście HTML musi zostać zaktualizowany, aby używać tej klasy:

<ol class="toc-list" role="list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title</span> <span class="page"><span class="visually-hidden">Page</span> 1</span> </a> <ol role="list"> <!-- subsection items --> </ol> </li>
</ol>

Mając ten fundament, zacząłem zwracać się do liderów między tytułem a stroną.

Kod zastępczy do osadzenia w CodePen

Tworzenie liderów kropek

Liderzy są tak powszechni w mediach drukowanych, że możesz się zastanawiać, dlaczego CSS już tego nie obsługuje? Odpowiedź to: to robi. Cóż, trochę.

Właściwie jest leader() funkcja zdefiniowana w Specyfikacja CSS Generated Content for Paged Media. Jednak, podobnie jak w przypadku wielu specyfikacji stronicowanych mediów, ta funkcja nie jest zaimplementowana w żadnych przeglądarkach, dlatego wykluczam ją jako opcję (przynajmniej w chwili, gdy to piszę). Nie ma go nawet na liście caniuse.com, prawdopodobnie dlatego, że nikt tego nie wdrożył i nie ma planów ani sygnałów, że to zrobi.

Na szczęście zarówno Julie, jak i Christoph poruszyli już ten problem w swoich postach. Aby wstawić lidery kropek, obaj użyli a ::after pseudoelement z jego content właściwość ustawiona na bardzo długi ciąg kropek, na przykład:

.toc-list li > a > .title { position: relative; overflow: hidden;
} .toc-list li > a .title::after { position: absolute; padding-left: .25ch; content: " . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . "; text-align: right;
}

Połączenia ::after pseudoelement jest ustawiony na pozycję bezwzględną, aby usunąć go z przepływu strony i uniknąć zawijania do innych wierszy. Tekst jest wyrównany do prawej, ponieważ chcemy, aby ostatnie kropki w każdym wierszu były wyrównane z liczbą na końcu wiersza. (Więcej o zawiłościach tego zagadnienia później). .title element jest ustawiony na pozycję względną, więc ::after pseudo-element nie wyłamuje się z pudełka. Tymczasem overflow jest ukryty, więc wszystkie te dodatkowe kropki są niewidoczne. Rezultatem jest ładny spis treści z liderami w postaci kropek.

Jest jednak jeszcze coś, co wymaga rozważenia.

Sara zwróciła mi również uwagę, że wszystkie te kropki liczą się jako tekst dla czytników ekranu. Więc co słyszysz? „Wprowadzenie kropka kropka kropka kropka…” do momentu ogłoszenia wszystkich kropek. To okropne doświadczenie dla użytkowników czytników ekranu.

Rozwiązaniem jest wstawienie dodatkowego elementu za pomocą aria-hidden Ustawić true a następnie użyj tego elementu, aby wstawić kropki. Tak więc kod HTML staje się:

<ol class="toc-list" role="list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title<span class="leaders" aria-hidden="true"></span></span> <span class="page"><span class="visually-hidden">Page</span> 1</span> </a> <ol role="list"> <!-- subsection items --> </ol> </li>
</ol>

A CSS staje się:

.toc-list li > a > .title { position: relative; overflow: hidden;
} .toc-list li > a .leaders::after { position: absolute; padding-left: .25ch; content: " . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . "; text-align: right;
}

Teraz czytniki ekranu będą ignorować kropki i oszczędzą użytkownikom frustracji związanej z wysłuchiwaniem wielu ogłaszanych kropek.

Kod zastępczy do osadzenia w CodePen

Ostatnie poprawki

W tym momencie składnik spisu treści wygląda całkiem nieźle, ale przydałoby się trochę drobnych szczegółów. Na początek większość książek wizualnie odsuwa tytuły rozdziałów od tytułów podrozdziałów, więc pogrubiłem pozycje najwyższego poziomu i wprowadziłem margines, aby oddzielić podsekcje od rozdziałów, które następowały po nich:

.toc-list > li > a { font-weight: bold; margin-block-start: 1em;
}

Następnie chciałem uporządkować wyrównanie numerów stron. Wszystko wyglądało w porządku, gdy używałem czcionki o stałej szerokości, ale w przypadku czcionek o zmiennej szerokości kropki wiodące mogły tworzyć wzór zygzakowaty, gdy dopasowywały się do szerokości numeru strony. Na przykład, każdy numer strony z 1 byłby węższy niż inne, w wyniku czego kropki wiodące nie są wyrównane z kropkami w poprzednich lub następnych wierszach.

Źle ustawione liczby i kropki w spisie treści.
Doskonały spis treści z HTML + CSS

Aby rozwiązać ten problem, ustawiłem font-variant-numeric do tabular-nums więc wszystkie liczby są traktowane z tą samą szerokością. Ustawiając również minimalną szerokość na 2ch, upewniłem się, że wszystkie liczby z jedną lub dwiema cyframi są idealnie wyrównane. (Możesz ustawić to na 3ch jeśli Twój projekt ma więcej niż 100 stron.) Oto ostateczny kod CSS dla numeru strony:

.toc-list li > a > .page { min-width: 2ch; font-variant-numeric: tabular-nums; text-align: right;
}
Wyrównane kropki wiodące w spisie treści.
Doskonały spis treści z HTML + CSS

I na tym spis treści jest kompletny!

Kod zastępczy do osadzenia w CodePen

Wnioski

Stworzenie spisu treści z wykorzystaniem wyłącznie HTML i CSS było większym wyzwaniem niż się spodziewałem, ale jestem bardzo zadowolony z rezultatu. To podejście jest nie tylko wystarczająco elastyczne, aby pomieścić rozdziały i podsekcje, ale także dobrze obsługuje podsekcje bez aktualizacji CSS. Ogólne podejście działa na stronach internetowych, na których chcesz prowadzić łącza do różnych lokalizacji treści, a także na plikach PDF, w których spis treści ma zawierać łącza do różnych stron. I oczywiście świetnie wygląda również w druku, jeśli masz ochotę użyć go w broszurze lub książce.

Chciałbym podziękować Julie Blanc i Christophowi Grabo za ich doskonałe posty na blogu dotyczące tworzenia spisu treści, ponieważ oba były nieocenione, gdy zaczynałem. Chciałbym również podziękować Sarze Soueidan za jej uwagi dotyczące ułatwień dostępu podczas pracy nad tym projektem.


Doskonały spis treści z HTML + CSS pierwotnie opublikowany w dniu Sztuczki CSS. Powinieneś pobierz biuletyn.

Znak czasu:

Więcej z Sztuczki CSS