En perfekt innholdsfortegnelse med HTML + CSS PlatoBlockchain Data Intelligence. Vertikalt søk. Ai.

En perfekt innholdsfortegnelse med HTML + CSS

Tidligere i år ga jeg selv ut en e-bok kalt Forstå JavaScript-løfter (gratis for nedlasting). Selv om jeg ikke hadde noen intensjon om å gjøre den om til en trykt bok, var det nok folk som tok kontakt for å spørre om en trykt versjon som jeg bestemte meg for å publisere selv. Jeg trodde det ville være en enkel øvelse med HTML og CSS for å generere en PDF og deretter sende den til skriveren. Det jeg ikke skjønte var at jeg ikke hadde svar på en viktig del av en trykt bok: innholdsfortegnelsen.

Sammensetningen av en innholdsfortegnelse

I kjernen er en innholdsfortegnelse ganske enkel. Hver linje representerer en del av en bok eller nettside og indikerer hvor du kan finne det innholdet. Vanligvis inneholder linjene tre deler:

  1. Tittelen på kapitlet eller delen
  2. Ledere (dvs. de prikkene, strekene eller linjene) som visuelt kobler tittelen til sidetallet
  3. Sidenummeret

En innholdsfortegnelse er lett å generere inne i tekstbehandlingsverktøy som Microsoft Word eller Google Docs, men fordi innholdet mitt var i Markdown og deretter transformert til HTML, var det ikke et godt alternativ for meg. Jeg ville ha noe automatisert som ville fungere med HTML for å generere innholdsfortegnelsen i et format som var egnet for utskrift. Jeg ønsket også at hver linje skulle være en lenke slik at den kunne brukes på nettsider og PDF-er for å navigere rundt i dokumentet. Jeg ville også ha punktledere mellom tittel og sidetall.

Og så begynte jeg å forske.

Jeg kom over to gode blogginnlegg om å lage en innholdsfortegnelse med HTML og CSS. Den første var "Bygg en innholdsfortegnelse fra HTML-en din" av Julie Blanc. Julie jobbet videre PagedJS, en polyfill for manglende sidesøkte mediefunksjoner i nettlesere som formaterer dokumenter på riktig måte for utskrift. Jeg begynte med Julies eksempel, men fant ut at det ikke helt fungerte for meg. Deretter fant jeg Christoph Grabo's "Responsive TOC-lederlinjer med CSS" post, som introduserte konseptet med å bruke CSS Grid (i motsetning til Julies float-baserte tilnærming) for å gjøre justering enklere. Nok en gang var tilnærmingen hans ikke helt riktig for mine formål.

Etter å ha lest disse to innleggene, følte jeg at jeg hadde en god nok forståelse av layoutproblemene til å ta fatt på egenhånd. Jeg brukte deler fra begge blogginnleggene i tillegg til å legge til noen nye HTML- og CSS-konsepter i tilnærmingen for å komme opp med et resultat jeg er fornøyd med.

Velge riktig markering

Når jeg skulle bestemme meg for riktig markering for en innholdsfortegnelse, tenkte jeg først og fremst på riktig semantikk. I bunn og grunn handler en innholdsfortegnelse om at en tittel (kapittel eller underavsnitt) er knyttet til et sidetall, nesten som et nøkkelverdi-par. Det førte meg til to alternativer:

  • Ett alternativ er å bruke en tabell (<table>) med én kolonne for tittelen og én kolonne for siden.
  • Så er det den ofte ubrukte og glemte definisjonslisten (<dl>) element. Det fungerer også som et nøkkelverdikart. Så nok en gang vil forholdet mellom tittelen og sidetallet være åpenbart.

Begge disse virket som gode alternativer helt til jeg innså at de egentlig bare fungerer for innholdsfortegnelser på ett nivå, nemlig bare hvis jeg ønsket å ha en innholdsfortegnelse med bare kapittelnavn. Hvis jeg ønsket å vise underseksjoner i innholdsfortegnelsen, hadde jeg imidlertid ingen gode alternativer. Tabellelementer er ikke gode for hierarkiske data, og selv om definisjonslister teknisk sett kan nestes, virket ikke semantikken riktig. Så jeg gikk tilbake til tegnebrettet.

Jeg bestemte meg for å bygge videre på Julies tilnærming og bruke en liste; men jeg valgte en bestilt liste (<ol>) i stedet for en uordnet liste (<ul>). Jeg tror en ordnet liste er mer passende i dette tilfellet. En innholdsfortegnelse representerer en liste over kapitler og underoverskrifter i den rekkefølgen de vises i innholdet. Rekkefølgen er viktig og bør ikke gå seg vill i markeringen.

Dessverre betyr bruk av en ordnet liste å miste det semantiske forholdet mellom tittelen og sidetallet, så mitt neste steg var å gjenopprette forholdet innenfor hvert listeelement. Den enkleste måten å løse dette på er å sette inn ordet "side" foran sidetallet. På den måten er forholdet mellom tallet i forhold til teksten tydelig, selv uten noe annet visuelt skille.

Her er et enkelt HTML-skjelett som dannet grunnlaget for min markering:

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

Bruke stiler på innholdsfortegnelsen

Når jeg hadde etablert markeringen jeg planla å bruke, var neste trinn å bruke noen stiler.

Først fjernet jeg de autogenererte tallene. Du kan velge å beholde de autogenererte tallene i ditt eget prosjekt hvis du vil, men det er vanlig at bøker har unummererte forord og etterord inkludert i kapittellisten, noe som gjør de autogenererte tallene feil.

For mitt formål ville jeg fylt inn kapittelnumrene manuelt og deretter justere oppsettet slik at toppnivålisten ikke har noen utfylling (og dermed justere den med avsnitt) og hver innebygde liste er rykket inn med to mellomrom. Jeg valgte å bruke en 2ch utfyllingsverdi fordi jeg fortsatt ikke var helt sikker på hvilken font jeg ville bruke. De ch lengdeenhet lar utfyllingen være i forhold til bredden på et tegn - uansett hvilken skrift som brukes - i stedet for en absolutt pikselstørrelse som kan ende med å se inkonsekvent ut.

Her er CSS-en jeg endte opp med:

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

Sara Soueidan påpekte for meg at WebKit-nettlesere fjerner listesemantikk når list-style-type is none, så jeg måtte legge til role="list" inn i HTML-en for å bevare den:

<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 av tittel og sidenummer

Med listen stilt etter min smak, var det på tide å gå videre til å style et enkelt listeelement. For hvert element i innholdsfortegnelsen må tittelen og sidetallet stå på samme linje, med tittelen til venstre og sidetallet justert til høyre.

Du tenker kanskje: "Ikke noe problem, det er det flexbox er for!" Du tar ikke feil! Flexbox kan faktisk oppnå riktig tittelsidejustering. Men det er noen vanskelige innrettingsproblemer når lederne legges til, så jeg valgte i stedet å gå med Christophs tilnærming ved å bruke et rutenett, som som en bonus da det også hjelper med titler med flere linjer. Her er CSS for et enkelt 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;
}

Rutenettet har to kolonner, hvorav den første er auto-størrelse for å fylle opp hele bredden av beholderen, minus den andre kolonnen, som er dimensjonert til max-content. Sidetallet er justert til høyre, slik det er tradisjonelt i en innholdsfortegnelse.

Den eneste andre endringen jeg gjorde på dette tidspunktet var å skjule "Side"-teksten. Dette er nyttig for skjermlesere, men unødvendig visuelt, så jeg brukte en tradisjonelle visually-hidden klasse for å skjule det fra visningen:

.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 må HTML-en oppdateres for å bruke den klassen:

<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 grunnlaget på plass, gikk jeg videre til å henvende meg til lederne mellom tittelen og siden.

CodePen Embed Fallback

Opprette punktledere

Ledere er så vanlige i trykte medier at du kanskje lurer på, hvorfor støtter ikke CSS det allerede? Svaret er: det gjør det. Vel, snill.

Det er faktisk en leader() funksjon definert i CSS-generert innhold for Paged Media-spesifikasjon. Men som med mye av spesifikasjonene for sidemedier, er ikke denne funksjonen implementert i noen nettlesere, og ekskluderer den derfor som et alternativ (i hvert fall på det tidspunktet jeg skriver dette). Det er ikke engang oppført på caniuse.com, antagelig fordi ingen har implementert det og det er ingen planer eller signaler om at de vil.

Heldigvis har både Julie og Christoph allerede tatt opp dette problemet i sine respektive innlegg. For å sette inn punktlederne brukte de begge en ::after pseudo-element med sitt content egenskap satt til en veldig lang streng med prikker, slik:

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

De ::after pseudo-elementet er satt til en absolutt posisjon for å ta det ut av flyten på siden og unngå omkobling til andre linjer. Teksten er justert til høyre fordi vi vil ha de siste prikkene på hver linje i flukt med tallet på slutten av linjen. (Mer om kompleksiteten i dette senere.) The .title element er satt til å ha en relativ posisjon slik at ::after pseudo-element bryter ikke ut av boksen. I mellomtiden har overflow er skjult slik at alle de ekstra prikkene er usynlige. Resultatet er en pen innholdsfortegnelse med punktledere.

Det er imidlertid noe annet som må vurderes.

Sara påpekte også for meg at alle disse prikkene teller som tekst til skjermlesere. Så hva hører du? "Introduksjon prikk prikk prikk prikk prikk..." til alle prikkene er annonsert. Det er en forferdelig opplevelse for brukere av skjermlesere.

Løsningen er å sette inn et tilleggselement med aria-hidden satt til true og bruk deretter det elementet til å sette inn prikkene. Så HTML-en blir:

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

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

Nå vil skjermlesere ignorere prikkene og spare brukere for frustrasjonen ved å lytte til flere prikker som blir annonsert.

CodePen Embed Fallback

Finpuss

På dette tidspunktet ser innholdsfortegnelseskomponenten ganske bra ut, men den kan trenge litt detaljarbeid. Til å begynne med, forskjøv de fleste bøker visuelt kapitteltitler fra underseksjonstitler, så jeg gjorde elementene på øverste nivå fete og introduserte en margin for å skille underseksjoner fra kapitlene som fulgte:

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

Deretter ønsket jeg å rydde opp i justeringen av sidetallene. Alt så greit ut da jeg brukte en skrift med fast bredde, men for fonter med variabel bredde kunne lederprikkene ende opp med å danne et sikksakkmønster når de tilpasser seg bredden på et sidetall. For eksempel vil ethvert sidetall med en 1 være smalere enn andre, noe som resulterer i lederprikker som er feiljustert med prikkene på forrige eller etterfølgende linjer.

Feiljusterte tall og prikker i en innholdsfortegnelse.
En perfekt innholdsfortegnelse med HTML + CSS

For å fikse dette problemet, satte jeg font-variant-numeric til tabular-nums så alle tall behandles med samme bredde. Ved også å sette minimumsbredden til 2ch, sørget jeg for at alle tall med ett eller to sifre er perfekt justert. (Det kan være lurt å sette dette til 3ch hvis prosjektet ditt har mer enn 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;
}
Justerte lederprikker i en innholdsfortegnelse.
En perfekt innholdsfortegnelse med HTML + CSS

Og dermed er innholdsfortegnelsen komplett!

CodePen Embed Fallback

konklusjonen

Å lage en innholdsfortegnelse uten annet enn HTML og CSS var en mer utfordring enn jeg forventet, men jeg er veldig fornøyd med resultatet. Ikke bare er denne tilnærmingen fleksibel nok til å imøtekomme kapitler og underseksjoner, men den håndterer underseksjoner pent uten å oppdatere CSS. Den overordnede tilnærmingen fungerer på nettsider der du ønsker å lenke til de ulike plasseringer av innhold, samt PDF-er der du ønsker at innholdsfortegnelsen skal lenke til forskjellige sider. Og selvfølgelig ser den også flott ut på trykk hvis du noen gang er tilbøyelig til å bruke den i en brosjyre eller bok.

Jeg vil gjerne takke Julie Blanc og Christoph Grabo for deres utmerkede blogginnlegg om å lage en innholdsfortegnelse, siden begge disse var uvurderlige da jeg begynte. Jeg vil også takke Sara Soueidan for hennes tilbakemeldinger om tilgjengelighet mens jeg jobbet med dette prosjektet.


En perfekt innholdsfortegnelse med HTML + CSS opprinnelig publisert på CSS-triks. Du burde få nyhetsbrevet.

Tidstempel:

Mer fra CSS triks