To read the guide, visit: https://ashok-khanna.github.io/pdf/
If you like this guide and repo, please star it!
Tutorial on paged.js
License: MIT License
To read the guide, visit: https://ashok-khanna.github.io/pdf/
If you like this guide and repo, please star it!
Hi, I hope you are doing well. I tried to use your code using MacOS Sonoma
I have this 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:
/**
* 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:
/* - * - * - * - * - * - * - * - * - * - */
/* 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;
}
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?
Thank you for your work. Is there a responsive solution planned?
I learned about the paged.js third-party library from other sources, but I'm still confused about how to use it!!
Could you please help me? Please forgive my poor English:), looking forward to your reply!!
In the console we can find some issues
Uncaught (in promise) DOMException: Failed to execute 'querySelectorAll' on 'DocumentFragment': '</pre></code>
<h3>Page Breaks</h3>
<p>By default' is not a valid selector.
at Breaks.processBreaks (https://pdf.math.dev/paged.polyfill.js:28816:27)
at Breaks.afterParsed (https://pdf.math.dev/paged.polyfill.js:28810:9)
at https://pdf.math.dev/paged.polyfill.js:364:26
at Array.forEach (<anonymous>)
at Hook.trigger (https://pdf.math.dev/paged.polyfill.js:363:15)
at Chunker.flow (https://pdf.math.dev/paged.polyfill.js:2583:33)
at async Previewer.preview (https://pdf.math.dev/paged.polyfill.js:31035:15)
at async https://pdf.math.dev/paged.polyfill.js:31096:11
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.