Printing selected Elements on a web page from JavaScript

András Szepesházi
5 min readMar 10, 2019

--

Recently I had a task where I had to print specific parts of a web page dynamically, from JavaScript. This certainly does not sound like the most complicated problem in the world. Still, when searching for solutions, I was surprised to find that popular approaches all have caveats that I wasn’t eager to put up with.

If you would— as I did — search on StackOverflow for “print specific div” or some similar expression, you’d find a dozen questions and a bunch of answers for each. And every high-voted answer will have an equally high-voted comment that goes along the lines of “this does not work in situations when…”.

An SO comment warning about using visibility:none for non-printable blocks
Another SO comment: a fair warning about replacing the current document body
And another SO comment refers to losing styles when creating a new window element for printing
Yet another SO comment about troubles when using a fixed div for printing

I found two main approaches for the above problem. One is to use media specific stylesheets and / or CSS definitions. The other is to create a new window object, insert the selected node’s html content into this new window’s document body, and call print() on the window.

Media specific styles

Using media specific stylesheet or media specific css definitions is a clean solution for defining your page’s print view, and does not require any JavaScript. When the user prints the page, all the @media print rules kick in and will define how your page will look like once printed. However, you ‘ll have to know in advance what is the desired print output for each page of your web application. You’ll also need to maintain all those print-specific CSS files and styles along your regular, non-print stylesheets. That is a lot of work. And if you need to have multiple print views of a page, depending on some application state or other condition, you are out of luck — pure CSS will not help you.

Insert selected content into a window object

This approach has the benefit of being dynamic — you can invoke it with any HTML node content. The simplest (and at the same time the most invasive and troubling) way is to reuse the current window object like this:

function printMe(element) {
var printContent = element.innerHTML;
var originalContent = window.document.body.innerHTML;
window.document.body.innerHTML = printContent;
window.print();
window.document.body.innerHTML = originalContent;
}

But please don’t do this. Wiping out and recreating the innerHTML of the document body will have serious side effects on most pages. For one, all dynamically attached event handlers will be removed from your nodes.

Other variants create a new window object, then create some body boilerplate, and the printable content gets injected into this body. A better approach, but does not preserve styles — you’ll need to inject the stylesheets from the original document to the new one. And the print window will open as a new tab or window, which is unattractive.

Not being able to find a satisfactory solution, I rolled my own. It is a tiny JavaScript module (75 lines of source code) with a supplementary CSS of ~10 lines, and works like this.

It has a single exposed function called PrintElements.print(elements) which expects an array of DOM Elements. If you’d print a single node obtained as var node = document.getElementById("someId"), you’d call the function as PrintElements.print([node]). If you’d print all nodes matching a certain class name, you’d write var nodes = document.getElementsByClassName("someClass"), then call the function as PrintElements.print(nodes).

The function iterates through the passed nodes, and for each node, it traverses up the DOM tree until the BODY element. At each level, including the initial one (which is the to-be-printed node’s level), it attaches a marker class (pe-preserve-print) to the current node. Then attaches another marker class (pe-no-print) to all siblings of the current node, but only if there is no pe-preserve-printclass on them. As a third act, it also attaches another class to preserved ancestor elements pe-preserve-ancestor.

Let’s say we have the following document tree.

DOM tree example borrowed from http://www.openbookproject.net/tutorials/getdown/css/lesson4.html

Now if we’d like to print only the second and third paragraph of the section element, we’d be calling PrintElements.print([p2Ref, p3Ref]) with those two elements. The function will attach the following classes to the DOM.

The css rules for these marker classes are dead simple. pe-no-printhas a display:noneproperty, and that’s it. pe-preserve-print has no style definitions, it simply serves as a marker, so we don’t remove any nodes that already have been preserved, when traversing the tree in multiple passes. pe-preserve-ancestor is a bit trickier, and I suspect there is no magic set of rules that will universally fit all websites. You can skip style definitions for this class altogether, and the print result will already be better than with any other methods. But I found that resetting ancestor borders, box-shadows, margin and padding usually brings a bit of extra improvement for the print view. Here is my suggested set of styles for this class.

.pe-preserve-ancestor {        
display: block !important;
margin: 0 !important;
padding: 0 !important;
border: none !important;
box-shadow: none !important;
}

You should experiment with styles for this class to find the best solution for your specific needs.

And now, reward time for getting through the article (or just scrolling down quickly). Check out PrintElements in action. You’ll find four buttons in the top semi-transparent menu that’ll print different sections of the page. Notice how only the requested content will be printed, and how all styles are preserved. Also check out the source code. Let me know if it works for your specific use cases.

Enjoy!

--

--

András Szepesházi

I'm a senior developer working for Skawa Innovation Ltd. with a growing taste for full stack Dart applications.