Ένας τέλειος πίνακας περιεχομένων με HTML + CSS PlatoBlockchain Data Intelligence. Κάθετη αναζήτηση. Ολα συμπεριλαμβάνονται.

Ένας τέλειος πίνακας περιεχομένων με HTML + CSS

Νωρίτερα φέτος, δημοσίευσα μόνος μου ένα ebook που ονομάζεται Κατανόηση των υποσχέσεων JavaScript (δωρεάν για λήψη). Παρόλο που δεν είχα καμία πρόθεση να το μετατρέψω σε έντυπο βιβλίο, αρκετοί άνθρωποι ήρθαν ρωτώντας για μια έντυπη έκδοση και αποφάσισα να τη δημοσιεύσω μόνος μου. Σκέφτηκα ότι θα ήταν μια εύκολη άσκηση χρησιμοποιώντας HTML και CSS για να δημιουργήστε ένα PDF και στη συνέχεια στείλτε το στον εκτυπωτή. Αυτό που δεν συνειδητοποίησα ήταν ότι δεν είχα απάντηση σε ένα σημαντικό μέρος ενός έντυπου βιβλίου: τον πίνακα περιεχομένων.

Το μακιγιάζ ενός πίνακα περιεχομένων

Στον πυρήνα του, ένας πίνακας περιεχομένων είναι αρκετά απλός. Κάθε γραμμή αντιπροσωπεύει ένα μέρος ενός βιβλίου ή μιας ιστοσελίδας και υποδεικνύει πού μπορείτε να βρείτε αυτό το περιεχόμενο. Συνήθως, οι γραμμές περιέχουν τρία μέρη:

  1. Ο τίτλος του κεφαλαίου ή της ενότητας
  2. Leader (δηλαδή εκείνες οι τελείες, οι παύλες ή οι γραμμές) που συνδέουν οπτικά τον τίτλο με τον αριθμό σελίδας
  3. Ο αριθμός σελίδας

Ένας πίνακας περιεχομένων είναι εύκολο να δημιουργηθεί μέσα σε εργαλεία επεξεργασίας κειμένου όπως το Microsoft Word ή τα Έγγραφα Google, αλλά επειδή το περιεχόμενό μου ήταν σε Markdown και στη συνέχεια μετατράπηκε σε HTML, αυτή δεν ήταν καλή επιλογή για μένα. Ήθελα κάτι αυτοματοποιημένο που θα λειτουργούσε με HTML για τη δημιουργία του πίνακα περιεχομένων σε μορφή κατάλληλη για εκτύπωση. Ήθελα επίσης κάθε γραμμή να είναι ένας σύνδεσμος, ώστε να μπορεί να χρησιμοποιηθεί σε ιστοσελίδες και αρχεία PDF για την πλοήγηση στο έγγραφο. Ήθελα επίσης κουκκίδες μεταξύ του τίτλου και του αριθμού σελίδας.

Και έτσι άρχισα να ερευνώ.

Βρήκα δύο εξαιρετικές αναρτήσεις ιστολογίου σχετικά με τη δημιουργία πίνακα περιεχομένων με HTML και CSS. Το πρώτο ήταν "Δημιουργήστε έναν πίνακα περιεχομένων από το HTML σας" από την Julie Blanc. Η Τζούλι δούλεψε PagedJS, μια πολυσυμπλήρωση για λειτουργίες σελιδοποιημένων μέσων που λείπουν σε προγράμματα περιήγησης ιστού που μορφοποιεί σωστά τα έγγραφα για εκτύπωση. Ξεκίνησα με το παράδειγμα της Τζούλι, αλλά διαπίστωσα ότι δεν μου λειτούργησε αρκετά. Στη συνέχεια, βρήκα αυτό του Christoph Grabo "Αποκριτικές γραμμές ηγέτη TOC με CSS" post, το οποίο εισήγαγε την έννοια της χρήσης CSS Grid (σε αντίθεση με την προσέγγιση που βασίζεται σε float της Julie) για να διευκολύνει την ευθυγράμμιση. Για άλλη μια φορά, όμως, η προσέγγισή του δεν ήταν σωστή για τους σκοπούς μου.

Αφού διάβασα αυτές τις δύο αναρτήσεις, όμως, ένιωσα ότι είχα αρκετά καλή κατανόηση των προβλημάτων διάταξης για να ξεκινήσω μόνος μου. Χρησιμοποίησα κομμάτια και από τις δύο αναρτήσεις ιστολογίου καθώς και πρόσθεσα μερικές νέες έννοιες HTML και CSS στην προσέγγιση για να καταλήξω σε ένα αποτέλεσμα με το οποίο είμαι ευχαριστημένος.

Επιλογή της σωστής σήμανσης

Όταν αποφάσισα για τη σωστή σήμανση για έναν πίνακα περιεχομένων, σκέφτηκα κυρίως τη σωστή σημασιολογία. Ουσιαστικά, ένας πίνακας περιεχομένων αφορά έναν τίτλο (κεφάλαιο ή υποενότητα) που συνδέεται με έναν αριθμό σελίδας, σχεδόν σαν ένα ζεύγος κλειδιού-τιμής. Αυτό με οδήγησε σε δύο επιλογές:

  • Μια επιλογή είναι να χρησιμοποιήσετε έναν πίνακα (<table>) με μία στήλη για τον τίτλο και μία στήλη για τη σελίδα.
  • Στη συνέχεια, υπάρχει η συχνά αχρησιμοποίητη και ξεχασμένη λίστα ορισμών (<dl>) στοιχείο. Λειτουργεί επίσης ως χάρτης κλειδιού-τιμής. Έτσι, για άλλη μια φορά, η σχέση μεταξύ τίτλου και αριθμού σελίδας θα ήταν προφανής.

Οποιοδήποτε από αυτά φαινόταν ως καλές επιλογές μέχρι που συνειδητοποίησα ότι πραγματικά λειτουργούν μόνο για πίνακες περιεχομένων ενός επιπέδου, δηλαδή, μόνο εάν ήθελα να έχω έναν πίνακα περιεχομένων με μόνο ονόματα κεφαλαίων. Ωστόσο, αν ήθελα να εμφανίσω υποενότητες στον πίνακα περιεχομένων, δεν είχα καλές επιλογές. Τα στοιχεία πίνακα δεν είναι ιδανικά για ιεραρχικά δεδομένα, και ενώ οι λίστες ορισμών μπορούν τεχνικά να είναι ένθετες, η σημασιολογία δεν φαινόταν σωστή. Έτσι, επέστρεψα στον πίνακα σχεδίασης.

Αποφάσισα να βασιστώ στην προσέγγιση της Julie και να χρησιμοποιήσω μια λίστα. Ωστόσο, επέλεξα μια ταξινομημένη λίστα (<ol>) αντί για μη ταξινομημένη λίστα (<ul>). Νομίζω ότι μια ταξινομημένη λίστα είναι πιο κατάλληλη σε αυτή την περίπτωση. Ένας πίνακας περιεχομένων αντιπροσωπεύει μια λίστα κεφαλαίων και υποτίτλων με τη σειρά με την οποία εμφανίζονται στο περιεχόμενο. Η παραγγελία έχει σημασία και δεν πρέπει να χαθεί στη σήμανση.

Δυστυχώς, η χρήση μιας ταξινομημένης λίστας σημαίνει ότι χάνεται η σημασιολογική σχέση μεταξύ του τίτλου και του αριθμού σελίδας, επομένως το επόμενο βήμα μου ήταν να αποκαταστήσω αυτήν τη σχέση σε κάθε στοιχείο της λίστας. Ο ευκολότερος τρόπος για να το λύσετε είναι απλώς να εισαγάγετε τη λέξη "σελίδα" πριν από τον αριθμό σελίδας. Με αυτόν τον τρόπο, η σχέση του αριθμού σε σχέση με το κείμενο είναι σαφής, ακόμη και χωρίς καμία άλλη οπτική διάκριση.

Ακολουθεί ένας απλός σκελετός HTML που αποτέλεσε τη βάση της σήμανσής μου:

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

Εφαρμογή στυλ στον πίνακα περιεχομένων

Μόλις καθιέρωσα τη σήμανση που σχεδίαζα να χρησιμοποιήσω, το επόμενο βήμα ήταν να εφαρμόσω ορισμένα στυλ.

Πρώτα, αφαίρεσα τους αριθμούς που δημιουργήθηκαν αυτόματα. Μπορείτε να επιλέξετε να διατηρήσετε τους αριθμούς που δημιουργούνται αυτόματα στο δικό σας έργο, αν θέλετε, αλλά είναι σύνηθες τα βιβλία να περιλαμβάνουν μη αρίθμητους προλόγους και ακολούθους λέξεις στη λίστα των κεφαλαίων, γεγονός που καθιστά τους αριθμούς που δημιουργούνται αυτόματα λανθασμένοι.

Για τον σκοπό μου, θα συμπλήρωνα τους αριθμούς των κεφαλαίων με μη αυτόματο τρόπο και στη συνέχεια θα προσαρμόζα τη διάταξη έτσι ώστε η λίστα ανώτατου επιπέδου να μην έχει καμία συμπλήρωση (άρα ευθυγραμμίζοντάς την με παραγράφους) και κάθε ενσωματωμένη λίστα έχει δύο κενά. Επέλεξα να χρησιμοποιήσω α 2ch τιμή padding γιατί ακόμα δεν ήμουν σίγουρος ποια γραμματοσειρά θα χρησιμοποιούσα. ο ch Η μονάδα μήκους επιτρέπει στη συμπλήρωση να είναι σε σχέση με το πλάτος ενός χαρακτήρα — ανεξάρτητα από τη γραμματοσειρά που χρησιμοποιείται — αντί για ένα απόλυτο μέγεθος pixel που θα μπορούσε να καταλήξει να φαίνεται ασυνεπές.

Εδώ είναι το CSS στο οποίο κατέληξα:

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

Σάρα Σουεϊντάν μου επισήμανε ότι τα προγράμματα περιήγησης WebKit αφαιρούν τη σημασιολογία της λίστας όταν list-style-type is none, οπότε έπρεπε να προσθέσω role="list" στο HTML για να το διατηρήσετε:

<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

Προσαρμογή του τίτλου και του αριθμού σελίδας

Με το στυλ της λίστας σύμφωνα με τις προτιμήσεις μου, ήρθε η ώρα να προχωρήσω στο στυλ ενός μεμονωμένου στοιχείου λίστας. Για κάθε στοιχείο στον πίνακα περιεχομένων, ο τίτλος και ο αριθμός σελίδας πρέπει να βρίσκονται στην ίδια γραμμή, με τον τίτλο στα αριστερά και τον αριθμό σελίδας στοιχισμένο στα δεξιά.

Μπορεί να σκέφτεστε, "Κανένα πρόβλημα, γι' αυτό είναι το flexbox!" Δεν έχεις άδικο! Το Flexbox μπορεί πράγματι να επιτύχει τη σωστή στοίχιση σελίδας τίτλου. Αλλά υπάρχουν μερικά δύσκολα ζητήματα ευθυγράμμισης όταν προστίθενται οι ηγέτες, γι' αυτό επέλεξα να ακολουθήσω την προσέγγιση του Christoph χρησιμοποιώντας ένα πλέγμα, το οποίο ως μπόνους, καθώς βοηθάει και με τίτλους πολλαπλών γραμμών. Εδώ είναι το CSS για ένα μεμονωμένο στοιχείο:

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

Το πλέγμα έχει δύο στήλες, η πρώτη από τις οποίες είναι auto-μεγέθους για να γεμίσει όλο το πλάτος του δοχείου, μείον τη δεύτερη στήλη, η οποία έχει μέγεθος max-content. Ο αριθμός σελίδας είναι στοιχισμένος προς τα δεξιά, όπως συνηθίζεται σε έναν πίνακα περιεχομένων.

Η μόνη άλλη αλλαγή που έκανα σε αυτό το σημείο ήταν η απόκρυψη του κειμένου "Σελίδα". Αυτό είναι χρήσιμο για αναγνώστες οθόνης, αλλά δεν είναι απαραίτητο οπτικά, γι' αυτό χρησιμοποίησα ένα παραδοσιακός visually-hidden τάξη για να το κρύψεις από τα μάτια:

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

Και, φυσικά, το HTML πρέπει να ενημερωθεί για να χρησιμοποιήσει αυτήν την κλάση:

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

Με αυτό το θεμέλιο στη θέση του, προχώρησα στο να απευθυνθώ στους ηγέτες μεταξύ του τίτλου και της σελίδας.

CodePen Embed Fallback

Δημιουργία οδηγών κουκκίδων

Οι ηγέτες είναι τόσο συνηθισμένοι στα έντυπα μέσα που ίσως αναρωτιέστε, γιατί το CSS δεν το υποστηρίζει ήδη; Η απάντηση είναι: κάνει. Λοιπόν, κάπως.

Στην πραγματικότητα υπάρχει ένα leader() συνάρτηση που ορίζεται στο Περιεχόμενο που δημιουργείται CSS για Προδιαγραφές Πολυμέσα σε σελίδα. Ωστόσο, όπως συμβαίνει με πολλές από τις σελιδοποιημένες προδιαγραφές πολυμέσων, αυτή η λειτουργία δεν εφαρμόζεται σε κανένα πρόγραμμα περιήγησης, επομένως αποκλείεται ως επιλογή (τουλάχιστον τη στιγμή που το γράφω). Δεν αναφέρεται καν caniuse.com, πιθανώς επειδή κανείς δεν το έχει εφαρμόσει και δεν υπάρχουν σχέδια ή σημάδια ότι θα το κάνει.

Ευτυχώς, τόσο η Julie όσο και ο Christoph έχουν ήδη ασχοληθεί με αυτό το πρόβλημα στις αντίστοιχες αναρτήσεις τους. Για να εισαγάγουν τους οδηγούς κουκκίδων, και οι δύο χρησιμοποίησαν α ::after ψευδοστοιχείο με το content Η ιδιότητα έχει οριστεί σε μια πολύ μεγάλη συμβολοσειρά κουκκίδων, όπως αυτό:

.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 Το ψευδοστοιχείο έχει οριστεί σε απόλυτη θέση για να το αφαιρέσει από τη ροή της σελίδας και να αποφύγει την αναδίπλωση σε άλλες γραμμές. Το κείμενο είναι στοιχισμένο προς τα δεξιά γιατί θέλουμε οι τελευταίες τελείες κάθε γραμμής να είναι στο ίδιο επίπεδο με τον αριθμό στο τέλος της γραμμής. (Περισσότερα για την πολυπλοκότητα αυτού αργότερα.) Το .title το στοιχείο έχει ρυθμιστεί να έχει μια σχετική θέση έτσι το ::after Το ψευδοστοιχείο δεν ξεφεύγει από το κουτί του. Εν τω μεταξύ, το overflow είναι κρυμμένο ώστε όλες αυτές οι επιπλέον κουκκίδες να είναι αόρατες. Το αποτέλεσμα είναι ένας όμορφος πίνακας περιεχομένων με κουκκίδες.

Ωστόσο, υπάρχει κάτι άλλο που χρειάζεται προσοχή.

Η Sara μου επεσήμανε επίσης ότι όλες αυτές οι κουκκίδες υπολογίζονται ως κείμενο για προγράμματα ανάγνωσης οθόνης. Τι ακούς λοιπόν; "Introduction dot dot dot dot..." μέχρι να ανακοινωθούν όλες οι τελείες. Αυτή είναι μια απαίσια εμπειρία για τους χρήστες του προγράμματος ανάγνωσης οθόνης.

Η λύση είναι να εισαγάγετε ένα επιπλέον στοιχείο με aria-hidden οριστεί σε true και στη συνέχεια χρησιμοποιήστε αυτό το στοιχείο για να εισαγάγετε τις τελείες. Έτσι το HTML γίνεται:

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

Και το CSS γίνεται:

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

Τώρα οι αναγνώστες οθόνης θα αγνοήσουν τις κουκκίδες και θα γλιτώσουν από τους χρήστες την απογοήτευση της ακρόασης πολλαπλών κουκκίδων που ανακοινώνονται.

CodePen Embed Fallback

Τελευταίες πινελιές

Σε αυτό το σημείο, το στοιχείο του πίνακα περιεχομένων φαίνεται αρκετά καλό, αλλά θα μπορούσε να χρησιμοποιήσει κάποια μικρή λεπτομέρεια. Αρχικά, τα περισσότερα βιβλία αντισταθμίζουν οπτικά τους τίτλους κεφαλαίων από τους τίτλους των υποενοτήτων, γι' αυτό έκανα έντονα τα στοιχεία ανώτατου επιπέδου και εισήγαγα ένα περιθώριο για να διαχωρίσω τις υποενότητες από τα κεφάλαια που ακολούθησαν:

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

Στη συνέχεια, ήθελα να καθαρίσω τη στοίχιση των αριθμών σελίδων. Όλα φαίνονταν εντάξει όταν χρησιμοποιούσα γραμματοσειρά σταθερού πλάτους, αλλά για γραμματοσειρές μεταβλητού πλάτους, οι κουκκίδες οδηγού θα μπορούσαν να καταλήξουν να σχηματίζουν ένα μοτίβο ζιγκ-ζαγκ καθώς προσαρμόζονται στο πλάτος ενός αριθμού σελίδας. Για παράδειγμα, οποιοσδήποτε αριθμός σελίδας με 1 θα ήταν πιο στενός από άλλους, με αποτέλεσμα κουκκίδες κορυφαίας που δεν ευθυγραμμίζονται σωστά με τις τελείες σε προηγούμενες ή επόμενες γραμμές.

Μη ευθυγραμμισμένοι αριθμοί και τελείες σε έναν πίνακα περιεχομένων.
Ένας τέλειος πίνακας περιεχομένων με HTML + CSS

Για να διορθώσω αυτό το πρόβλημα, ρύθμισα font-variant-numeric προς την tabular-nums οπότε όλοι οι αριθμοί αντιμετωπίζονται με το ίδιο πλάτος. Ορίζοντας επίσης το ελάχιστο πλάτος σε 2ch, εξασφάλισα ότι όλοι οι αριθμοί με ένα ή δύο ψηφία είναι τέλεια ευθυγραμμισμένοι. (Μπορεί να θέλετε να το ρυθμίσετε σε 3ch εάν το έργο σας έχει περισσότερες από 100 σελίδες.) Ακολουθεί το τελικό CSS για τον αριθμό σελίδας:

.toc-list li > a > .page { min-width: 2ch; font-variant-numeric: tabular-nums; text-align: right;
}
Ευθυγραμμισμένες κουκκίδες οδηγού σε έναν πίνακα περιεχομένων.
Ένας τέλειος πίνακας περιεχομένων με HTML + CSS

Και με αυτό, ο πίνακας περιεχομένων ολοκληρώνεται!

CodePen Embed Fallback

Συμπέρασμα

Η δημιουργία ενός πίνακα περιεχομένων με τίποτα άλλο εκτός από HTML και CSS ήταν μεγαλύτερη πρόκληση από ό,τι περίμενα, αλλά είμαι πολύ ευχαριστημένος με το αποτέλεσμα. Όχι μόνο αυτή η προσέγγιση είναι αρκετά ευέλικτη ώστε να χωράει κεφάλαια και υποενότητες, αλλά χειρίζεται όμορφα τις υποενότητες χωρίς να ενημερώνει το CSS. Η συνολική προσέγγιση λειτουργεί σε ιστοσελίδες όπου θέλετε να συνδέσετε τις διάφορες τοποθεσίες περιεχομένου, καθώς και σε PDF όπου θέλετε ο πίνακας περιεχομένων να συνδέεται με διαφορετικές σελίδες. Και φυσικά, φαίνεται υπέροχο και σε έντυπη μορφή, αν έχετε την τάση να το χρησιμοποιήσετε σε φυλλάδιο ή βιβλίο.

Θα ήθελα να ευχαριστήσω τη Julie Blanc και τον Christoph Grabo για τις εξαιρετικές αναρτήσεις τους στο blog σχετικά με τη δημιουργία ενός πίνακα περιεχομένων, καθώς και οι δύο ήταν ανεκτίμητες όταν ξεκίνησα. Θα ήθελα επίσης να ευχαριστήσω τη Sara Soueidan για τα σχόλιά της σχετικά με την προσβασιμότητα καθώς εργαζόμουν σε αυτό το έργο.


Ένας τέλειος πίνακας περιεχομένων με HTML + CSS δημοσιεύθηκε αρχικά στις CSS-Κόλπα. Θα έπρεπε λάβετε το ενημερωτικό δελτίο.

Σφραγίδα ώρας:

Περισσότερα από Κόλπα CSS