I have a large webpage generated through many Vue components. The rendered HTML structure is somewhat similar to this:
<header></header>
<element1></element1>
<element2></element2>
<element3></element3>
<table></table>
<element4></element4>
<footer></footer>
I want to print this nicely on an A5 page having header
and footer
repeated on every printed page. I've tried this with 2 approaches:
Converting HTML structure into page containers and splitting elements based on total clientHeight
. Something like
<section class="page">
<header></header>
<element1></element1>
<element2></element2>
<element3></element3>
<footer></footer>
</section>
<section class="page">
<header></header>
<table></table>
<element4></element4>
<footer></footer>
</section>
Or, Adding CSS page break properties to header
and dynamically inserting at content overflow locations. For example
<header></header>
<element1></element1>
<element2></element2>
<element3></element3>
<footer></footer>
<header style="page-break-before: always"></header>
<table></table>
<element4></element4>
<footer></footer>
The DOM traversal to find overflow point looks like
var availHeight = 20; // Height of A5 page - tolerance
var body = document.querySelector('body');
var initialWidth = body.style.width;
body.style.width = '14.85cm';
if (body.clientHeight > availHeight) { // if content exceeds page height
var scrollHeight = 0;
for (var i = 0; i < body.children.length; i++) {
var child = body.children[i];
scrollHeight += child.clientHeight;
if (scrollHeight > availHeight) { // if children traversed till now make up to the height of page
if (child.clientHeight < availHeight) {
child.insertAdjacentHTML('beforebegin', '"page break html here..."');
scrollHeight = 0;
}
}
}
}
body.style.width = initialWidth;
However, both approaches give inaccurate results while printing.
CSS @page
is not supported by Safari, hence cannot be useful.
display: table-header-group
and table-footer-group
doesn't work either.
Is there a different cross-browser solution to achieve nicely printed pages with repeating headers and footers and not clipping/overlapping any content, or if the DOM traversal code can be improved to be more generic and stable? Can things be corrected or simplified here?
Thanks in advance. I haven't reached a satisfactory workaround or solution for this problem.
Not sure if this qualifies for a separate answer or should just be a comment with some (hopefully useful) tips.
So... this is a battle web developers have long fought. You can find various answers under this question, which this question actually seems to be a duplicate of (somewhat).
Disclaimer: I can't test the techniques mentioned on iOS, Safari nor IE myself atm. They're more of a recollection of my previous experiences and knowledge gathered from SO posts and various articles' comment threads.
I'll try to provide some pointers and techniques to get you slightly closer to full browser support, but there are no promises until @print
is fully supported :/
@page
support, the page margin also pushes down the fixed content. And without support, you'd have to resolve to some display: table
hacks maybe. I've replicated the fixed
solution in a simple example (I inlined the CSS so you could open it in a separate HTML file locally for easier testing):https://jsfiddle.net/vjvu8uox/
@media print
is something you could look into alongside @print
, since it's supported since CSS2. It doesn't provide the same powerful selectors, but allows you to tweak your styles slightly for print-specific purposes.
"The ‘print’ and ‘screen’ media types are defined in HTML4. The complete list of media types in HTML4 is: ‘aural’, ‘braille’, ‘handheld’, ‘print’, ‘projection’, ‘screen’, ‘tty’, ‘tv’. CSS2 defines the same list, deprecates ‘aural’ and adds ‘embossed’ and ‘speech’. Also, ‘all’ is used to indicate that the style sheet applies to all media types."
Tables. A lot of browsers position the thead
and tfoot
elements at the top and bottom of every page on print. This works if your thead
and tfoot
don't contain multiple tr
elements. The support for this landed in Chrome this March (2018). The useful keywords for this are:
thead { display: table-header-group; }
tfoot { display: table-footer-group; }
I'm wondering if you could maybe combine 1. and 3. to firstly position your header and footer with fixed
so they'd repeat and then maybe create empty elements to act as table-header-group
and table-footer-group
, which could provide paddings for the fixed elements?
I'll quickly also address your traversal script by saying that you might want to account for your Vue's app container (unless you've mounted your Vue instance to the body element, which is considered somewhat of a bad practise) and all its nested children. In that sense, @MunimMunna's jQuery snippet serves you better. I'd rewrite it as vanilla JS and using semi-modern utility methods, we could easily fix the Safari support for that script (I do not have the time right now, but can look into it coming Monday when I'm on my work rig/Mac to test it on Safari).
Best of luck in your print-support endeavours!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With