Skip to content

Instantly share code, notes, and snippets.

@theinvensi
Last active April 15, 2024 22:15
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save theinvensi/e1aacc43bb5a3d852e2e85b08cf85c8a to your computer and use it in GitHub Desktop.
Save theinvensi/e1aacc43bb5a3d852e2e85b08cf85c8a to your computer and use it in GitHub Desktop.
pagedjs-repeat-table-header
class RepeatTableHeadersHandler extends Paged.Handler {
constructor(chunker, polisher, caller) {
super(chunker, polisher, caller)
this.splitTablesRefs = []
}
afterPageLayout(pageElement, page, breakToken, chunker) {
this.chunker = chunker
this.splitTablesRefs = []
if (breakToken) {
const node = breakToken.node
const tables = this.findAllAncestors(node, "table")
if (node.tagName === "TABLE") tables.push(node)
if (tables.length > 0) {
this.splitTablesRefs = tables.map(t => t.dataset.ref)
let thead = node.tagName === "THEAD" ? node : this.findFirstAncestor(node, "thead")
if (thead) {
let lastTheadNode = thead.hasChildNodes() ? thead.lastChild : thead
breakToken.node = this.nodeAfter(lastTheadNode, chunker.source)
}
this.hideEmptyTables(pageElement, node)
}
}
}
hideEmptyTables(pageElement, breakTokenNode) {
this.splitTablesRefs.forEach(ref => {
let table = pageElement.querySelector("[data-ref='" + ref + "']")
if (table) {
let sourceBody = table.querySelector("tbody > tr")
if (!sourceBody || this.refEquals(sourceBody.firstElementChild, breakTokenNode)) {
table.style.visibility = "hidden"
table.style.position = "absolute"
let lineSpacer = table.nextSibling
if (lineSpacer) {
lineSpacer.style.visibility = "hidden"
lineSpacer.style.position = "absolute"
}
}
}
})
}
refEquals(a, b) {
return a && a.dataset && b && b.dataset && a.dataset.ref === b.dataset.ref
}
findFirstAncestor(element, selector) {
while (element.parentNode && element.parentNode.nodeType === 1) {
if (element.parentNode.matches(selector)) return element.parentNode
element = element.parentNode
}
return null
}
findAllAncestors(element, selector) {
const ancestors = []
while (element.parentNode && element.parentNode.nodeType === 1) {
if (element.parentNode.matches(selector)) ancestors.unshift(element.parentNode)
element = element.parentNode
}
return ancestors
}
layout(rendered, layout) {
this.splitTablesRefs.forEach(ref => {
const renderedTable = rendered.querySelector("[data-ref='" + ref + "']")
if (renderedTable) {
if (!renderedTable.getAttribute("repeated-headers")) {
const sourceTable = this.chunker.source.querySelector("[data-ref='" + ref + "']")
this.repeatColgroup(sourceTable, renderedTable)
this.repeatTHead(sourceTable, renderedTable)
renderedTable.setAttribute("repeated-headers", true)
}
}
})
}
repeatColgroup(sourceTable, renderedTable) {
let colgroup = sourceTable.querySelectorAll("colgroup")
let firstChild = renderedTable.firstChild
colgroup.forEach((colgroup) => {
let clonedColgroup = colgroup.cloneNode(true)
renderedTable.insertBefore(clonedColgroup, firstChild)
})
}
repeatTHead(sourceTable, renderedTable) {
let thead = sourceTable.querySelector("thead")
if (thead) {
let clonedThead = thead.cloneNode(true)
renderedTable.insertBefore(clonedThead, renderedTable.firstChild)
}
}
nodeAfter(node, limiter) {
if (limiter && node === limiter) return
let significantNode = this.nextSignificantNode(node)
if (significantNode) return significantNode
if (node.parentNode) {
while ((node = node.parentNode)) {
if (limiter && node === limiter) return
significantNode = this.nextSignificantNode(node)
if (significantNode) return significantNode
}
}
}
nextSignificantNode(sib) {
while ((sib = sib.nextSibling)) { if (!this.isIgnorable(sib)) return sib }
return null
}
isIgnorable(node) {
return (
(node.nodeType === 8)
|| ((node.nodeType === 3) && this.isAllWhitespace(node))
)
}
isAllWhitespace(node) {
return !(/[^\t\n\r ]/.test(node.textContent))
}
}
Paged.registerHandlers(RepeatTableHeadersHandler)
@axessweb
Copy link

great !

@zackwong97
Copy link

thanks bro you are the best

@anteeek
Copy link

anteeek commented Aug 19, 2023

Very epic, thanks

@TheCardinalSystem
Copy link

Is there support for tfoot as well?

@Zarky2k2
Copy link

greattttttttttttttttttt

@cathyhax
Copy link

Works like a charm! Thank you!!!

@kevinguto
Copy link

You just don't know how much of a savior you are sir! Thank you!!!!

@adepranaya
Copy link

You save my life, Thank u very much

@DaisukiDaYo
Copy link

Thanks a lot!!! you're my life saver!!! Kudos 🙏🙏🙏

@RobertBouillon
Copy link

RobertBouillon commented Mar 4, 2024

Works great most of the time. Nested repeated headers causes some sections to be missing from both pages, though :(

EDIT: nevermind - it's a bug with pagedJS, not this script!

@DiesuaY
Copy link

DiesuaY commented Mar 11, 2024

Thanks alot

@MoamenAbdelsattar
Copy link

Thanks, I haven't tested yet but I don't understand where the function layout is called. It's not a part of the documented API of hooks.

@mersa2024
Copy link

How do I get this to work? I add it to my code, but it doesn't do anything??? Is there something I need to add to the CSS?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment