Search code examples
javascripthtmlcssprintingpage-break

How to put a blank space atop of each page of a printed HTML


I've designed an HTML report with several divs and tables. Now my client asks me to put a repeating header at the top of each printed page. This is not possible using plain CSS and HTML as far as I know. Just as a try, I put header div element inside a thead to which I applied display: table-header-group in order to be displayed, and put all other elements of the report as rows of the main table but no success.

  1. A workaround is to use @print { .header {position: fixed; top: 10px} } in order to repeat .header element at the top of each page. But for this work, we should put a blank space at the top of each new page; otherwise fixed header element is mixed with other elements at the top of table.

  2. As another workaround I can compute elements height at render time and put manual page breaks where needed. So I want to know if there is any Javascript library available to execute at page load and computes all render-time heights of div elements of the page and put a zero-height div element with page-break-before: always; before each div which exceeds height of an A4 paper. For suppose the following divs result in 14, 10, 8, 9, 6, 13, and 6 centimeter height at render time. I want the library put a page-break dummy dive element at specified locations:

<div id="d1">...</div>

<div id="d2">...</div>

<!-- here, because 14+10+8 exceeds 30cm -->

<div id="d3">...</div>

<div id="d4">...</div>

<div id="d5">...</div>

<!-- here, because 8+9+6+13 exceeds 30cm -->

<div id="d6">...</div>

<div id="d7">...</div>


Solution

  • Here is my final solution. The following code is executed at page load. My page was solely composed of a number of divs. Two divs as headers and the other divs (containing tabular data) as details. I assumed always printing in portrait scheme and also assumed A4 size (= ~21x30cm). I also assumed 4.5cm margins for left-right and top-bottom of the page. The code first resizes divs to a portrait compatible width (so that all tables are divs are resized automatically). Then I iterate over non-header div elements to add a page break when height exceeds A4 height. I also cloned and added header elements atop of each page through this code.

    // page width in pixels
    function pw() {
        return cm2px(16.5);
    }
    // page height in pixels
    function ph() {
        return cm2px(25.5);
    }
    function px2cm(px) {
        return px * 2.54 / 96;
    }
    function cm2px(cm) {
        return cm * 96 / 2.54;
    }
    
    $(function() {
        $('.section > div').each(function(){
            $(this).width(pw() + 'px');
        });
        hh = $('.section > div.heading');
        headingHeight = hh.eq(0).height() + hh.eq(1).height();
    
        h1 = hh.eq(0).clone();
        h2 = hh.eq(1).clone();
    
        var h = headingHeight;
        var pageHeight = ph();
    
        $('.section > div').not('.heading').each(function(){
            if (h + $(this).height() + 14 > pageHeight) {
                h1.css('page-break-before', 'always');
                $(this).before(h1.clone());
                $(this).before(h2.clone());
                h = $(this).height() + 14 + headingHeight;
            } else {
                h += $(this).height() + 14;
            }
        });
    });