En perfekt indholdsfortegnelse med HTML + CSS PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

En perfekt indholdsfortegnelse med HTML + CSS

Tidligere i år udgav jeg selv en e-bog kaldet Forstå JavaScript-løfter (gratis til download). Selvom jeg ikke havde til hensigt at omdanne den til en trykt bog, var der nok folk, der kontaktede for at forespørge om en trykt version, som jeg besluttede at udgive selv. Jeg troede, det ville være en nem øvelse at bruge HTML og CSS til at generere en PDF og derefter sende den til printeren. Hvad jeg ikke var klar over, var, at jeg ikke havde et svar på en vigtig del af en trykt bog: indholdsfortegnelsen.

Sammensætningen af ​​en indholdsfortegnelse

I sin kerne er en indholdsfortegnelse ret enkel. Hver linje repræsenterer en del af en bog eller webside og angiver, hvor du kan finde indholdet. Typisk indeholder linjerne tre dele:

  1. Titlen på kapitlet eller afsnittet
  2. Ledere (dvs. de prikker, bindestreger eller linjer), der visuelt forbinder titlen med sidetallet
  3. Sidenummeret

En indholdsfortegnelse er let at generere inde i tekstbehandlingsværktøjer som Microsoft Word eller Google Docs, men fordi mit indhold var i Markdown og derefter omdannet til HTML, var det ikke en god mulighed for mig. Jeg ville have noget automatiseret, der kunne arbejde med HTML til at generere indholdsfortegnelsen i et format, der var egnet til print. Jeg ønskede også, at hver linje skulle være et link, så den kunne bruges på websider og PDF'er til at navigere rundt i dokumentet. Jeg ville også have prikledere mellem titel og sidetal.

Og så begyndte jeg at undersøge.

Jeg stødte på to fremragende blogindlæg om oprettelse af en indholdsfortegnelse med HTML og CSS. Den første var "Byg en indholdsfortegnelse ud fra din HTML" af Julie Blanc. Julie arbejdede på PagedJS, en polyfill til manglende sidesidede mediefunktioner i webbrowsere, der korrekt formaterer dokumenter til udskrivning. Jeg startede med Julies eksempel, men fandt ud af, at det ikke helt virkede for mig. Dernæst fandt jeg Christoph Grabo's "Responsive TOC-lederlinjer med CSS" post, som introducerede konceptet med at bruge CSS Grid (i modsætning til Julies float-baserede tilgang) for at gøre justering lettere. Endnu en gang var hans tilgang dog ikke helt rigtig til mine formål.

Efter at have læst disse to indlæg følte jeg dog, at jeg havde en god nok forståelse af layoutproblemerne til at gå i gang på egen hånd. Jeg brugte stykker fra begge blogindlæg samt tilføjede nogle nye HTML- og CSS-koncepter i tilgangen til at komme med et resultat, jeg er tilfreds med.

Valg af korrekt opmærkning

Da jeg besluttede mig for den korrekte opmærkning til en indholdsfortegnelse, tænkte jeg primært på den korrekte semantik. Grundlæggende handler en indholdsfortegnelse om, at en titel (kapitel eller underafsnit) er bundet til et sidetal, næsten som et nøgle-værdi-par. Det førte mig til to muligheder:

  • En mulighed er at bruge en tabel (<table>) med en kolonne for titlen og en kolonne for siden.
  • Så er der den ofte ubrugte og glemte definitionsliste (<dl>) element. Det fungerer også som et nøgleværdikort. Så endnu en gang ville forholdet mellem titlen og sidetallet være indlysende.

Hver af disse virkede som gode muligheder, indtil jeg indså, at de i virkeligheden kun virker for indholdsfortegnelser på et niveau, nemlig kun hvis jeg ville have en indholdsfortegnelse med kun kapitelnavne. Hvis jeg ville vise underafsnit i indholdsfortegnelsen, havde jeg dog ingen gode muligheder. Tabelelementer er ikke gode til hierarkiske data, og selvom definitionslister teknisk set kan indlejres, virkede semantikken ikke korrekt. Så jeg gik tilbage til tegnebrættet.

Jeg besluttede at bygge videre på Julies tilgang og bruge en liste; dog valgte jeg en bestilt liste (<ol>) i stedet for en uordnet liste (<ul>). Jeg tror, ​​at en ordnet liste er mere passende i dette tilfælde. En indholdsfortegnelse repræsenterer en liste over kapitler og underoverskrifter i den rækkefølge, de optræder i indholdet. Rækkefølgen er vigtig og bør ikke gå tabt i opmærkningen.

Desværre betyder det at bruge en ordnet liste at miste det semantiske forhold mellem titlen og sidetallet, så mit næste skridt var at genetablere dette forhold inden for hvert listepunkt. Den nemmeste måde at løse dette på er blot at indsætte ordet "side" før sidetallet. På den måde er forholdet mellem tallet i forhold til teksten klart, selv uden anden visuel skelnen.

Her er et simpelt HTML-skelet, der dannede grundlaget for min opmærkning:

<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>

Anvendelse af stilarter på indholdsfortegnelsen

Når jeg havde etableret den markup, jeg planlagde at bruge, var næste skridt at anvende nogle stilarter.

Først fjernede jeg de autogenererede numre. Du kan vælge at beholde de autogenererede numre i dit eget projekt, hvis du ønsker det, men det er almindeligt, at bøger har unummererede for- og efterord inkluderet på listen over kapitler, hvilket gør de autogenererede numre forkerte.

Til mit formål ville jeg udfylde kapitelnumrene manuelt og derefter justere layoutet, så listen på øverste niveau ikke har nogen udfyldning (og dermed justere den med afsnit), og hver indlejret liste er indrykket med to mellemrum. Jeg valgte at bruge en 2ch udfyldningsværdi, fordi jeg stadig ikke var helt sikker på, hvilken skrifttype jeg ville bruge. Det ch længdeenhed tillader, at polstringen er i forhold til bredden af ​​et tegn - uanset hvilken skrifttype der bruges - snarere end en absolut pixelstørrelse, der kan ende med at se inkonsekvent ud.

Her er den CSS, jeg endte med:

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

Sara Soueidan påpegede for mig, at WebKit-browsere fjerner listesemantik, når list-style-type is none, så jeg var nødt til at tilføje role="list" ind i HTML for at bevare det:

<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>
CodePen Embed Fallback

Styling af titel og sidenummer

Med listen stylet efter min smag, var det tid til at gå videre til at style et individuelt listeelement. For hvert punkt i indholdsfortegnelsen skal titel og sidenummer stå på samme linje, med titlen til venstre og sidetallet justeret til højre.

Du tænker måske, "Intet problem, det er hvad flexbox er til!" Du tager ikke fejl! Flexbox kan faktisk opnå den korrekte titelsidejustering. Men der er nogle vanskelige tilpasningsproblemer, når lederne tilføjes, så jeg valgte i stedet at gå med Christophs tilgang ved at bruge et gitter, hvilket som en bonus, da det også hjælper med multiline-titler. Her er CSS for et individuelt element:

.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;
}

Gitteret har to kolonner, hvoraf den første er auto-størrelse til at fylde hele beholderens bredde, minus den anden kolonne, som er dimensioneret til max-content. Sidenummeret er justeret til højre, som det er traditionelt i en indholdsfortegnelse.

Den eneste anden ændring, jeg lavede på dette tidspunkt, var at skjule "Side"-teksten. Dette er nyttigt for skærmlæsere, men unødvendigt visuelt, så jeg brugte en traditionelle visually-hidden klasse for at skjule det for øje:

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

Og selvfølgelig skal HTML'en opdateres for at bruge den klasse:

<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>

Med dette fundament på plads gik jeg videre til at henvende mig til lederne mellem titlen og siden.

CodePen Embed Fallback

Oprettelse af prikledere

Ledere er så almindelige i trykte medier, at du måske spekulerer på, hvorfor understøtter CSS ikke allerede det? Svaret er: det gør det. Nå, slags.

Der er faktisk en leader() funktion defineret i CSS-genereret indhold til Paged Media-specifikation. Men som med mange af de sidede mediespecifikationer er denne funktion ikke implementeret i nogen browsere, og derfor udelukker den som en mulighed (i hvert fald på det tidspunkt, jeg skriver dette). Det er ikke engang opført på caniuse.com, formentlig fordi ingen har implementeret det, og der er ingen planer eller signaler om, at de vil.

Heldigvis har både Julie og Christoph allerede adresseret dette problem i deres respektive indlæg. For at indsætte priklederne brugte de begge en ::after pseudo-element med sit content egenskab indstillet til en meget lang række af prikker, som denne:

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

::after pseudo-element er sat til en absolut position for at tage det ud af strømmen på siden og undgå at ombryde til andre linjer. Teksten er justeret til højre, fordi vi ønsker, at de sidste prikker af hver linje flugter med tallet i slutningen af ​​linjen. (Mere om kompleksiteten af ​​dette senere.) Den .title element er indstillet til at have en relativ position, så ::after pseudo-elementet bryder ikke ud af sin boks. I mellemtiden overflow er skjult, så alle de ekstra prikker er usynlige. Resultatet er en smuk indholdsfortegnelse med prikledere.

Der er dog noget andet, der skal overvejes.

Sara gjorde mig også opmærksom på, at alle disse prikker tæller som tekst til skærmlæsere. Så hvad hører du? "Introduktion dot dot dot dot...", indtil alle prikkerne er annonceret. Det er en forfærdelig oplevelse for skærmlæserbrugere.

Løsningen er at indsætte et ekstra element med aria-hidden indstillet til true og brug derefter dette element til at indsætte prikkerne. Så HTML'en bliver:

<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>

Og CSS bliver:

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

Nu vil skærmlæsere ignorere prikkerne og spare brugerne for frustration ved at lytte til flere prikker, der bliver annonceret.

CodePen Embed Fallback

Efterbehandling

På dette tidspunkt ser indholdsfortegnelseskomponenten ret godt ud, men den kunne bruge lidt detaljeret arbejde. Til at starte med forskyder de fleste bøger visuelt kapiteltitler fra underafsnitstitler, så jeg gjorde emnerne på øverste niveau fed og introducerede en margen til at adskille underafsnit fra de efterfølgende kapitler:

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

Dernæst ville jeg rydde op i justeringen af ​​sidetallene. Alt så okay ud, da jeg brugte en skrifttype med fast bredde, men for skrifttyper med variabel bredde kunne ledepunkterne ende med at danne et zigzag-mønster, når de tilpasser sig bredden af ​​et sidetal. For eksempel ville ethvert sidetal med et 1 være smallere end andre, hvilket resulterer i indføringspunkter, der er forkert justeret med prikkerne på forrige eller følgende linjer.

Fejljusterede tal og prikker i en indholdsfortegnelse.
En perfekt indholdsfortegnelse med HTML + CSS

For at løse dette problem, indstillede jeg font-variant-numeric til tabular-nums så alle tal behandles med samme bredde. Ved også at indstille minimumsbredden til 2ch, sikrede jeg, at alle tal med et eller to cifre er perfekt justeret. (Du ønsker måske at indstille dette til 3ch hvis dit projekt har mere end 100 sider.) Her er den endelige CSS for sidenummeret:

.toc-list li > a > .page { min-width: 2ch; font-variant-numeric: tabular-nums; text-align: right;
}
Justerede lederprikker i en indholdsfortegnelse.
En perfekt indholdsfortegnelse med HTML + CSS

Og dermed er indholdsfortegnelsen komplet!

CodePen Embed Fallback

Konklusion

At lave en indholdsfortegnelse uden andet end HTML og CSS var mere en udfordring, end jeg havde forventet, men jeg er meget tilfreds med resultatet. Denne tilgang er ikke kun fleksibel nok til at rumme kapitler og underafsnit, men den håndterer underafsnit pænt uden at opdatere CSS. Den overordnede tilgang fungerer på websider, hvor du vil linke til de forskellige steder af indhold, samt PDF'er, hvor du ønsker, at indholdsfortegnelsen skal linke til forskellige sider. Og selvfølgelig ser det også godt ud på tryk, hvis du nogensinde har lyst til at bruge det i en brochure eller bog.

Jeg vil gerne takke Julie Blanc og Christoph Grabo for deres fremragende blogindlæg om oprettelse af en indholdsfortegnelse, da begge disse var uvurderlige, da jeg gik i gang. Jeg vil også gerne takke Sara Soueidan for hendes tilgængelighedsfeedback, mens jeg arbejdede på dette projekt.


En perfekt indholdsfortegnelse med HTML + CSS oprindeligt udgivet den CSS-tricks. Du burde få nyhedsbrevet.

Tidsstempel:

Mere fra CSS-tricks