Pretty Printing Your Site with JavaScript


Visit one of your favorite sites, print a page, and check the format of the print preview. Chances are it doesn’t come out very well-formatted. Understandably, printing things from the web may not be something your users do on a regular basis, but for the sites with users who do print confirmation pages, item lists, quotes, etc. there is a better way to pretty print your site with JavaScript!


TLDR;

Using JavaScript alone, I’ll show you how to create a pretty print page derived from elements currently in the DOM of any page. There is no need to make any additional network requests and no need for any server-side development. By simply cloning the elements of the page that you want to print, you can rebuild your pretty print page seamlessly.

This process cannot override the default browser print functionality, but it provides a more customized and useful print experience for your users.


The traditional approach

Printing exactly what’s on the page and leaving it up to the browser to figure out what to do is often not the best approach. The generic approach to combat this is to struggle with a print.css file and hide enough elements to make your page appear presentable. The print.css approach can be a hassle, and all too often, still only gets you halfway to where you want to be.

Upgrading your print functionality

Create a blank canvas 

First off, you’ll need to create a “canvas” to paint on since the page you want to print already has its own DOM filled with elements that you may or may not need. Elements like the header, footer, call-to-action buttons, unused filters, navigation bars, tab bars, backgrounds, and large images, etc. may not serve much of a purpose on a printed page. There are two traditional approaches to making your page print-ready: 

1) Add a @media print media query in an existing CSS file to hide all of the unneeded elements on the page.

@media print {
/* This media query only applies itself to the page during printing */
header, footer, nav, .btn--call-to-action {
display: none !important;
}
}

2) Include an external stylesheet with the media attribute value of print and house all of the CSS you want to apply to the page during printing, inside of it.

<link href="styles/print.css" rel="stylesheet" media="print" type="text/css">

I do not recommend leaving these two options as your only approach for a few reasons:

  • Altering the DOM for printing may be fine for basic pages, but the solution is not scalable. If your page has a layout beyond just a few simple elements, your print.css file can quickly grow in both size and complexity.
  • Site content typically has to stay in the same general area for printing. You can use something like Flexbox’s order and flex-direction properties to shift items around a bit, but we quickly run into limitations. 
  • You have to maintain your print styles in addition to your pages. If you add new elements to a page, your print styles will have to account for them as well.

We can look beyond the traditional approaches for now and move forward with creating this “blank canvas.” The first option that may come to mind for creating a blank canvas is to pop open a new window with window.open(). This approach may work, but we now have to consider pop-up blockers, sizing and positioning the new window, and the need to destroy the window once the user is done. Let’s shoot for a more seamless user experience. If we can’t use a new window, then how about a good ole iframe? 

Create an object to house your functionality:

See the Pen NeroScript.io Pretty Print by D’ontreye Nero (@dnero) on CodePen.

I wrote this with vanilla JavaScript to avoid having to pull in any dependencies and avoided using template literals and arrow functions due to the lack of IE support. I’m also using string concatenation for simplicity. In your project, you should use Handlebars (or some other templating engine) and consider the browsers you’ll need to support.

Let’s break down all of the internal methods here.

  • NsPrettyPrintPage.print() — this function prints the current page you’re on by creating an empty/hidden iframe and executing the print command
  • NsPrettyPrintPage.generatePrintLayout() — dynamically gathers body and footer html to be injected into iframe
  • NsPrettyPrintPage.generateHeaderHtml() —imports CSS from the current DOM to be applied to the iframe header
  • NsPrettyPrintPage.generateGlobalCss() — adds global classes like margins, image dimensions, and table breaking, to be present on every page
  • NsPrettyPrintPage.generateFooterHtml() — adds a global footer to every page
  • NsPrettyPrintPage.prettyPrintCodeForSamplePageType() — NsPrettyPrintPage.{{YOUR FN NAME HERE}}() allows you to customize each page on your site for pretty printing by page type

Identify elements you want on your printed pages

This is where things take a 180 from the traditional “print.css” approach. Instead of starting with the current DOM and hiding or rearranging the parts we don’t want to show, let’s take the “blank canvas” and create new pretty print page from there. 

Let’s assume our hypothetical product management team discovers that our users only want to see the product image, title, price, and rating in easy-to-scan columns when using our print feature.

We need to divert our thinking from what is useful when a user is surfing online to what is useful for a user with a physical, printed copy of our information.

While constructing the new page, remember to keep the use cases in mind here. Why do users really need to print this page? What are they doing with the paper? Do they need space to write notes? The page as-is on the web is probably not very useful on paper. No buttons will work, nor will the background images do much more than waste ink. 

Here is a sample print preview of a search for “keyboard” on amazon.com that returned 16 results.

amazon.com's search result print preview.

A few notes from the print output above: 

  • Our users will need seven sheets of paper to print this view
  • The Add to Cart buttons have been hidden, but we still have several elements that are not beneficial to our print process, like filters, navigation, and blank space which we could be using to display more items
  • This is not very useful for users who want to easily scan and compare items

Diving into the html for this page, we see that the result list is an unordered list with the id #s-results-list-atf.

HTML source from amazon.com's search result list.

We need to extract content from each list item, so let’s try to find a selector that is unique, but not so individual. If we check the classes, you’ll notice that all of the li tags have the class s-result-item, the ads have a class of AdHolder, and there is a top-rated brands section with the class acs-private-brands-container-background. Using a CSS querySelector, we can extract all of the items that are not ads or miscellaneous with the selector: #s-results-list-atf li.s-result-item:not(.AdHolder):not(.acs-private-brands-container-background).

Adding this page to our print feature requires a new function, plus a reference to it in our generatePrintLayout() function. Since we’re going to be printing this content and not worrying about responsiveness as much as we typically would, let’s stick with a simple table.

amazonSearchResults: function() {
let html = ''; // reference to all result items that are not ads
let resultItems = document.querySelectorAll('#s-results-list-atf li.s-result-item:not(.AdHolder):not(.acs-private-brands-container-background)');

html += '<table>';

// iterate over result items
resultItems.forEach(function(item) {
html += '<tr>';

// product image
html += '<td>' + item.querySelector('.s-access-image').outerHTML + '</td>';

// product title
html += '<td style="max-width: 400px;">' + item.querySelector('.s-access-title').innerHTML + '</td>';

// product price
html += '<td>' + item.querySelector('.sx-price-currency').innerHTML;
html += item.querySelector('.sx-price-whole').innerHTML + '.';
html += item.querySelector('.sx-price-fractional').innerHTML + '</td>';

// product rating
html += '<td style="min-width: 150px;">' + item.querySelector('.a-spacing-mini .a-icon-alt').innerHTML + '</td>';

html += '</tr>';
});

html += '</table>';

return html;
}

We only needed to extract the useful items requested by our “product management team” which slims our new print output down to two pages!

Pretty print version of amazon.com's search result page

This is a simple example of how you can transform your custom print feature without bulky solutions that will add yet another dependency. If you’re already using jQuery or any other library, it will fit right in with this solution. Simply swap out document.querySelectorAll('.myClass') with $('.myClass') for example and carry on. To make life easier, your DOM elements may need “data-” attributes on important elements to make them more easily identifiable, but you can use any JavaScript/HTML you’d like to target and extract what you need. Also note the use of innerHTML when the contents of a tag are needed, but outerHTML if you want to keep the tags as well, like when targeting a header<h2>My site header</h2>. outerHTML will drop the tag into the pretty print page as is.

Drawbacks

This feature can be implemented as a (custom) “print” button on your site, but it will not cover printing through default browser methods like right click > print, File > Print, and Ctrl/Cmd + P. The window.onbeforeprint() method can apply changes before the print job begins, but there is no way to cancel or override the default browser print job.

Testing this out for yourself

If you want to try this function on a pre-existing page or even a live site, I recommend using a Chrome extension called User JavaScript and CSS. Another option is to simply paste the NsPrettyPrint into your browser’s console. This has been tested in modern versions of Chrome, Safari, Firefox, and Edge, with graceful degradation for IE 11+.


Wrapping up

Now that you have a useful pretty print approach, feel free to extend this set-up however you’d like to fit your site. It is fairly easy to get up and running in no time. This print feature is designed to provide an identical experience to the default browser functionality. Be sure to scope this functionality properly or integrate it with your pre-existing print button or command function. Leave a comment or shoot me an email with any questions or comments.

Happy printing!

Leave a Reply

Your email address will not be published. Required fields are marked *