pdf
pdf copied to clipboard
Blank pages before table of contents if the TOC is longer than 1 page
Introduction
Hi, I hope you are doing well. I tried to use your code using MacOS Sonoma
I have this HTML:
HTML
<html>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Nunito:wght@100;300;400;500;700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" type="text/css" href="../style.css" />
<script src="../toc.js"></script>
<script src="../paged.polyfill.js"></script>
<script>
class handlers extends Paged.Handler { constructor(chunker, polisher,
caller) { super(chunker, polisher, caller); } beforeParsed(content) {
createToc({ content: content, tocElement: "#toc", titleElements:
["h2:not(.cover-header)", "h3:not(.cover-header)"], }); } }
Paged.registerHandlers(handlers);
</script>
</head>
<body>
<!-- 1.0 Cover Page -->
<div class="cover-page">
<h1>Standard Operation Procedures</h1>
<h3 class="cover-header no-counter">Hosted PBX</h3>
<h3 class="cover-header no-counter">{{time}}</h3>
<img src="../0.svg" />
<img src="../1.png" />
</div>
<!-- 2.0 Table of Contents -->
<div class="table-of-contents">
<h1 class="toc-header" id="toc-start">Table of Contents</h1>
<ol id="toc">
<!-- toc.js will automatically insert TOC here -->
</ol>
</div>
...rest
</body>
</html>
The script for generating TOC:
Script
/**
* Create a table of contents (TOC) based on the title elements within a specified content.
*
* @param {Object} config - Configuration object for the function.
* @param {Element} config.content - The root element where the TOC will be generated from.
* @param {string} config.tocElement - A CSS selector that specifies the element where the TOC will be appended.
* @param {Array} config.titleElements - An array of CSS selectors indicating title elements.
*/
function createToc(config) {
// Destructure the configuration object for easier access
const { content, tocElement, titleElements } = config;
// Locate the container where the TOC will be appended
let tocElementDiv = content.querySelector(tocElement);
// Create a new unordered list to house the TOC
let tocUl = document.createElement("ul");
tocUl.id = "list-toc-generated"; // Give it an ID for potential styling or scripting
tocElementDiv.appendChild(tocUl); // Append the list to the container
// Counter to generate unique IDs for title elements (if needed)
let tocElementNbr = 0;
// Loop over each title element selector
for (var i = 0; i < titleElements.length; i++) {
// Calculate the title hierarchy based on its position in the titleElements array
let titleHierarchy = i + 1;
// Select all elements in the content that match the current title selector
let titleElement = content.querySelectorAll(titleElements[i]);
titleElement.forEach(function (element) {
// Add a generic class and data attribute to indicate its hierarchy
element.classList.add("title-element");
element.setAttribute("data-title-level", titleHierarchy);
// If the element doesn't have an ID, create one
tocElementNbr++;
idElement = element.id;
if (idElement == "") {
element.id = "title-element-" + tocElementNbr;
}
});
}
// Select all title elements we previously modified
let tocElements = content.querySelectorAll(".title-element");
// Loop over each title element to create corresponding TOC list items
for (var i = 0; i < tocElements.length; i++) {
let tocElement = tocElements[i];
// Create a new list item for the TOC
let tocNewLi = document.createElement("li");
// Add generic and hierarchy-specific classes
tocNewLi.classList.add("toc-element");
tocNewLi.classList.add(
"toc-element-level-" + tocElement.dataset.titleLevel,
);
// Copy over other classes from the original title element (excluding the generic title-element class)
let classTocElement = tocElement.classList;
for (var n = 0; n < classTocElement.length; n++) {
if (classTocElement[n] != "title-element") {
tocNewLi.classList.add(classTocElement[n]);
}
}
// Set the inner content of the list item to be a link to the original title element
tocNewLi.innerHTML =
'<a href="#' + tocElement.id + '">' + tocElement.innerHTML + "</a>";
// Append the new list item to the TOC list
tocUl.appendChild(tocNewLi);
}
}
The CSS:
CSS
/* - * - * - * - * - * - * - * - * - * - */
/* 1.0 CSS to make A4 print preview as default web view */
@media screen {
.pagedjs_pages {
display: flex;
width: calc(var(--pagedjs-width));
flex: 0;
flex-wrap: wrap;
margin: 0 auto;
justify-content: center;
}
.pagedjs_page {
margin: 10mm;
border: solid 1px gray;
}
}
/* - * - * - * - * - * - * - * - * - * - */
/* 2.0 General Formatting */
html {
font-family: "Nunito", sans-serif;
height: auto;
}
h1 {
border-bottom: 1px solid black;
margin-bottom: 2rem;
padding-bottom: 1rem;
}
h2 {
border-bottom: 0.1875rem solid black;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
}
p {
text-align: justify;
line-height: 1.4rem;
}
a {
color: #347dbd;
}
a:hover {
color: #fc814a;
}
mark {
color: purple;
background-color: inherit;
}
light-mark {
color: purple;
}
img {
max-width: calc(100% - 3rem);
}
.img-75mm {
height: 75mm;
}
center {
border: 0.5px solid gray;
padding: 1.5rem;
}
pre {
background-color: #f0f0f0;
font-family: "Custom Mono", monospace;
padding: 1rem;
font-size: smaller;
white-space: pre-wrap;
}
code {
margin-bottom: 1.5rem;
display: block;
}
figure {
margin-block-start: 2rem;
margin-block-end: 2rem;
margin-inline-start: 0rem;
margin-inline-end: 0rem;
}
figcaption {
caption-side: top;
padding-bottom: 0.75rem;
border-bottom: 0.5px solid gray;
margin-bottom: 1.5rem;
color: gray;
font-weight: bold;
}
/* - * - * - * - * - * - * - * - * - * - */
/* 3.1 Page Size */
@page {
size: A4;
}
/* 3.2 Page Breaks */
.page-break {
break-after: page;
}
h2:not(.no-break) {
break-before: page;
margin-block-start: 0rem;
margin-block-end: 1.66rem;
}
h2.no-break:not(.top) {
margin-block-start: 3rem;
margin-block-end: 1.66rem;
}
h2.top {
margin-block-start: 0rem;
margin-block-end: 1.66rem;
}
h3:not(.top) {
margin-block-start: 2.49rem;
margin-block-end: 0.83rem;
}
h3.top {
margin-block-start: 1.66rem;
margin-block-end: 0.83rem;
}
/* 3.3 Page Numbers */
@page {
@bottom-right {
content: counter(page);
}
}
/* We use the counter-reset class to prevent the page
numbers starting from the cover page, but rather
from the next page (table of contents) */
.counter-reset {
counter-reset: page 1;
}
/* - * - * - * - * - * - * - * - * - * - */
/* 5.1 Table of Contents */
.table-of-contents {
page: table-of-contents;
}
@page table-of-contents {
@top-right {
content: none;
}
@bottom-left {
content: none;
}
}
/* Additional CSS: Advanced Styling for TOC */
.toc-header {
margin-bottom: 1rem;
border-bottom: 0.375rem solid black;
}
.table-of-contents a,
.table-of-contents a:hover {
font-family: "Nunito", sans-serif;
}
ol,
ul {
padding-inline-start: 0px;
}
ol,
ul a {
text-decoration: none;
}
.toc-element-level-1 a,
.toc-element-level-2 a {
color: black;
}
/* set the style for the list numbering to none */
#list-toc-generated {
list-style: none;
}
#list-toc-generated .toc-element a::after {
content: target-counter(attr(href), page);
float: right;
}
#list-toc-generated .toc-element-level-1 {
border-bottom: 0.0625rem solid black;
margin-top: 2rem;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
font-weight: bold;
}
/* counters */
#list-toc-generated {
counter-reset: counterTocLevel1;
}
#list-toc-generated .toc-element-level-1 {
counter-increment: counterTocLevel1;
counter-reset: counterTocLevel2;
}
#list-toc-generated .toc-element-level-1::before {
content: counter(counterTocLevel1) ". ";
padding-right: 5px;
}
#list-toc-generated .toc-element-level-2 {
counter-increment: counterTocLevel2;
margin-bottom: 0.5rem;
}
#list-toc-generated .toc-element-level-2::before {
content: counter(counterTocLevel1) "." counter(counterTocLevel2) " ";
padding-right: 5px;
}
/* hack for leaders */
#list-toc-generated {
overflow-x: hidden;
}
/* fake leading */
#list-toc-generated .toc-element-level-2::after {
content: ".............................................."
".............................................."
".............................................." "........";
float: left;
width: 0;
padding-left: 5px;
letter-spacing: 2px;
}
#list-toc-generated .toc-element {
display: flex;
}
#list-toc-generated .toc-element a::after {
position: absolute;
right: 0;
background-color: white;
padding-left: 6px;
}
#list-toc-generated .toc-element a {
right: 0;
}
The Problem
Everything went well if I generate a table of contents worth 1 page:
But if there is more than 1 page, I got two blank pages then the full TOC:
As you can see, the 3.15 is missing. It is on page 64. You can see that there are number 64 in page 4, which means it renders the page number but not the link.
Any ideas what went wrong?