-
-
Save theinvensi/e1aacc43bb5a3d852e2e85b08cf85c8a to your computer and use it in GitHub Desktop.
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) |
Is there support for tfoot
as well?
greattttttttttttttttttt
Works like a charm! Thank you!!!
You just don't know how much of a savior you are sir! Thank you!!!!
You save my life, Thank u very much
Thanks a lot!!! you're my life saver!!! Kudos 🙏🙏🙏
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!
Thanks alot
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.
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?
Thanks!
one note: When the table is moved to a new page (because the table does not fit on the previous page), the header appears twice.
you can avoid this by adding a condition.
repeatTHead(sourceTable, renderedTable) {
let thead = sourceTable.querySelector('thead');
if (thead && renderedTable.firstChild.tagName !== 'THEAD') {
let clonedThead = thead.cloneNode(true);
renderedTable.insertBefore(clonedThead, renderedTable.firstChild);
}
}
So great ! Thanks a lot !
Dear all,
Seeing everybody thanking for this I think it works, but I have absolutely no experience with js and so far no success in using this code.
What I tried is: simply putting it in the section right after the paged.polyfill.js section like this:
<script src="js/paged.polyfill.js"></script>
<script type="text/javascript">
class RepeatTableHeadersHandler extends Paged.Handler {
constructor(chunker, polisher, caller) {
.
.
.
</script>
And of course I added a table in the html with a header like this but longer:
<table>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr>
<td>Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
</table>
Could anybody point me in the right direction please?
Dear all,
Seeing everybody thanking for this I think it works, but I have absolutely no experience with js and so far no success in using this code.
What I tried is: simply putting it in the section right after the paged.polyfill.js section like this:
<script src="js/paged.polyfill.js"></script> <script type="text/javascript"> class RepeatTableHeadersHandler extends Paged.Handler { constructor(chunker, polisher, caller) { . . . </script>
And of course I added a table in the html with a header like this but longer:
<table> <tr> <th>Firstname</th> <th>Lastname</th> <th>Age</th> </tr> <tr> <td>Jill</td> <td>Smith</td> <td>50</td> </tr> </table>
Could anybody point me in the right direction please?
Add me on Discord and I can walk you through it. My username is cardinalsystem
.
Great work, thanks for sharing.
It's repeating the the first table header twice for me.
I think it does that when you have
thead {
display: table-header-group; /* Repeat header */
}
tbody {
display: table-row-group;
}
in your css
Very epic, thanks