Tips for printing from web applications


Web developers usually have to format user content for printing; for example, accountants might want physical copies of online ledgers while teachers might need lecture note printouts.

The challenge lies in getting consistent print output across a range of browsers and their never-ending stream of subtle nuances.

This post approaches printing from three viewpoints: tooling, JavaScript and CSS. It also describes the pitfalls and quirks to watch out for when printing across the big 5 browser platforms (i.e. Chrome, Edge, Firefox, Internet Explorer and Safari).

1. Tooling

Quick question – what does this page look like when printed?

Did you do a CTRL + P to figure that out? Print previews work excellently if you want to ‘see’ what a page looks like when printed. But what if you wanted to interact with the page and change it? Say you wanted to toy with the CSS or even debug JavaScript. Clearly, the preview option comes short; even if it doesn’t, going to preview mode every time you make changes is not fun.

Good news, Chrome provides print emulation tooling which allows you to style a page for printing. To activate the print emulation mode, do the following:

  • Press F12 to bring up the Chrome dev tools
  • Press Esc to bring up the extra tabs
  • Select the Emulation tab
  • Select the media option
  • Check the CSS media check box
  • Select print (or any other media target you desire)

chrome tools

The Chrome emulation mode should reveal the major print issues however, you should sanity check other browsers’ in print preview mode. This should catch the few remaining quirks, things like forgetting –webkit prefixes for Safari.

JavaScript

Media Listeners

The big 5 browsers all have media listener support, this allows detecting media properties such as orientation, resolution and viewport dimensions. For example, the snippet below will be triggered when the browser width falls below 960px.

var widthHandler = function(mql) {
    if(mql.matches) {
        console.log('Viewport width is <= 960px');
    } else {
        console.log('Viewport width is > 960px');
    }
}

var mql = window.matchMedia('(width: 960px)');
mql.addListener(widthHandler);

Chrome and Safari

The interesting aspect is the print media query option; Chrome and Safari can detect print events via these listeners. Unfortunately, IE, FF and Edge, even though they support other media queries, do not offer print media support.

Safari tends to fire the media listener event ‘after’ the print dialog appears. This effectively renders any desired print pre-processing useless – what’s the use if the print dialog is already visible?

Chrome offers the best support, modification of print options (e.g. paper type or layout) will still trigger appropriate print events; this is very useful if you want to restyle your page dynamically based on print options. Alas, only Chrome gives this option.

Here is how you attach handlers for the Chrome/Safari scenario.

var printHandler = function(mql) {
    if(mql.matches) {
        console.log('Print');
    } else {
        console.log('Not print');
    }
};

var mql = window.matchMedia('print');
mql.addListener(printHandler);

When you are all done, remember to clean things up by calling mql.removeListener(printHandler).

IE, FF and Edge

These 3 browsers expose the onbeforeprint and onafterprint events.

window.onbeforeprint = function () {
    console.log('Print started');
};

window.onafterprint = function () {
    console.log('PRINT DONE');
};

You would expect the onafterprint handler to be called immediately after the print dialog is disposed right? Nope, it is nearly always invoked immediately after onbeforeprint. Invocation order: onbeforeprint -> onafterprint -> print dialog opens.

Print events have a mind of their own…

Cross-browser printing

Merging both approaches gives a combination that should work well in most scenarios. See below:

function beforePrint () {
    console.log('before print');
}
function afterPrint() {
    console.log('after print');
}
window.onbeforeprint = beforePrint;
window.onafterprint = afterPrint;

var printHandler = function(mql) {
    if(mql.matches) {
        beforePrint();
    } else {
       afterPrint();
    }
};

var mql = window.matchMedia('print');
mql.addListener(printHandler);

CSS media

CSS media queries provide very powerful print styling capabilities. You should handle most of the styling issues with CSS and wrap up the thornier edge cases with JavaScript.

The introduction of the print media block allows you to control and override existing DOM styles. This makes it possible to hide certain elements on print, elements to new positions or even change the entire page’s layout.

@media print {
    body {
        width: 960px !important;
    }
}

Now in print mode, your page will have a width of 960px. Go ahead, go toy and play with this.

Note that you might need to add !important to make sure print styling overrides existing styling. For inline styles, !important might not work however increasing the specificity of the CSS selectors would eventually work.

Happy printing!

Related

Detecting Print Requests with JavaScript

MDN documentation on CSS Media

How to set up a print style sheet

Advertisements

6 thoughts on “Tips for printing from web applications

  1. Vaguely related: I don’t program web pages or JavaScript, but – I often run into emails (Thunderbird) and web pages (Firefox & Vivaldi) which print in something that looks like one point type (sometimes barely readable with a magnifying glass!) Is there any relatively easy way to fix this?

    For now, I go back to the page and copy and paste it into LibreOffice Writer which auto-adjusts the fonts, etc. and makes the printed page readable.

    (Kubuntu 12.04 Linux with backports – latest browser updates)

    Like

    • Hey Joseph,

      That might be because there are no print media queries and the browser is making a best attempt to print something.

      Apart from what you currently do; I do not know of a workaround (ideally the page authors should optimize for print).

      I hope this helps and thanks for the feedback!

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s