Latest TweetsAll o' me plugins freshly updated and ready for WP 5.0 :) profiles.wordpress.org/special…
Perishable Press

Unobtrusive JavaScript for ‘Print-This’ Links

One of the oldest JavaScript tricks in the book involves providing a “print this!” link for visitors that enables them to summon their operating system’s default print dialogue box to facilitate quick and easy printing of whatever page they happen to be viewing. With the old way of pulling this little stunt, we write this in the markup comprising the target “print this!” link in question:

<a href="javascript:window.print()">Print This!</a>

Big yuck there, of course, due to the obtrusive nature of the JavaScript implementation. Adhering to the principles of proper Web Standards, it is better practice to separate behavior from structure by placing this amazing “print this!” function in its own location, either in the <head> element, or even better in an external JavaScript file. So basically, we want markup that looks more like this:

<a href="http://domain.tld/target/" title="Print this page">Print This!</a>

Notice the new value for the href attribute. Rather than pointing illogically to the JavaScript function, it now points to an actual resource, which may be anything you desire. Previously, users without JavaScript would click the “print this!” link and blink while nothing happens. With the unobtrusive technique, you provide the location to which users without JavaScript shall go. Possibilities here include an explanation page or even just the page itself, depending on how lazy you wanna be.

Oh yeah, the code. Removing the obtrusive JavaScript from the anchor is not enough to make this trick work. We also need a nice, unobtrusive JavaScript function to make it happen:

// unobtrusive JavaScript for "Print This!" links
// https://perishablepress.com/press/2009/02/01/unobtrusive-javascript-for-print-this-links/

function printClick() {
	if (!document.getElementById) return false;
	if (!document.getElementById("print_this")) return false;

	var link = document.getElementById("print_this");
	link.onclick = function() {
		window.print();
		return false;
	}
	link.onkeypress = link.onclick;
}

// onload function (not needed if script called at the end of the page)
// replace with "printClick();" if script called at the end of the page

function addLoadEvent(func) {
	var oldonload = window.onload;
	if (typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function() {
			if (oldonload) {
				oldonload();
			}
			func();
		}
	}
}
addLoadEvent(printClick);

Nice. So what are we doing here? Well, there are two functions involved in the process. The first adds the “print this!” functionality to all target links, while the second is a generic onload-event function that triggers the “print this!” function as soon as the page has loaded. And of course, if your JavaScript file already contains this functionality, there is no need to replicate it here; simply call the printClick() function on page load.

In the first function, the first three lines apply the principle of “graceful degradation” by checking browser/device support for the three required JavaScript functions. If any of these three functions are not available, the script quietly stops, thereby preventing errors and other problems on unable devices. Then, the second two lines simply set the function variables, which together target the specific “print this!” link in the document. Finally, the function loops through all identifiable link targets and applies the desired “print this!” functionality via JavaScript’s window.print(); function.

Update: Thanks to the reminder from Tuan Anh, we have eliminated the for loop and now target the specified anchor element directly, thereby producing a lighter, cleaner and all-around better function. Thanks Tuan Anh! :)
Update: Thanks to the heroic help of Anatoli Papirovski, we may eliminate the onload() function from the script when calling it from the bottom of the document (as prescribed in the following section). If you are calling the script from the <head> section, the onload() function will need to be included. Thank you, Anatoli Papirovski! :)

To implement this technique, place the previous code into an external JavaScript file, say, “javascript.js” (so creative) and then link to the file at the bottom1 of your (X)HTML document like so:

			.
			.
			.
		</div>
		<script src="javascript.js" type="text/javascript"></script>
	</body>
</html>

Almost there.. the last thing you need to do is identify the “print this!” link by adding an id attribute of “print_this”. Place this code wherever you would like the “print this!” link to appear:

<a id="print_this" href="http://domain.tld/target/" title="Print this page">Print This!</a>

..and of course, you should also change the location of the href attribute to reflect the location that non-JavaScript users will go to upon clicking the link. For WordPress-powered sites, you may use the <?php the_permalink(); ?> template tag for the href value, thereby enabling “print this” links for every post on your site.

That’s all there is to it, really. Test that everything works and marvel in your users’ new ability to easily print your pages using this remarkable unobtrusive JavaScript technique. Ahhhh, what a feeling! ;)

Another Update!

Another useful unobtrusive “Print This!” technique. Here, we are using unobtrusive JavaScript to apply a “Print This!” button (not a link — an actual button element) next to a small bit of text that reminds people to print a copy of the page for their records. Note that the version of this method given at the Opera Dev site does not work in certain browsers due to a small error in syntax. In this version, the error has been fixed and the script should work properly in all modern web browsers.

<p id="print_this">Thank you for your order. Please print this page for your records.</p>

<script type="text/javascript">
(function printClick() {
	if (!document.getElementById("print_this")) return false;
	var link = document.getElementById("print_this");
	if(link && typeof(window.print === 'function')) {
		var button = document.createElement('input');
		button.setAttribute('type','button');
		button.setAttribute('value','Print This!');
		button.onclick = function() {
			window.print();
		};
		link.appendChild(button);
	}
})();
</script>

Footnotes

  • 1 This is the recommended location for linking to external JavaScript files according to the whole YSlow paradigm. Rest assured, however, that it is completely okay to link to the script via the document <head> as well.

Jeff Starr
About the Author Jeff Starr = Creative thinker. Passionate about free and open Web.
Archives
23 responses
  1. Anatoli Papirovski February 2, 2009 @ 9:14 am

    It does not matter if you link, you’re including the script at the bottom of your file, as such the DOM is already parsed and created. Therefore the onload element is unnecessary. If you were including the script in the head or before the element is created, you might need the onload event, but this way you don’t.

  2. Anatoli Papirovski February 2, 2009 @ 9:19 am

    And just for fun, here’s an article on *cough* About.com *cough* talking about this basic concept: http://javascript.about.com/library/blonldom.htm

  3. Jeff Starr

    I beg to differ. Test the method for yourself, both with and without the onload function. You will clearly see that the technique does not work without it. If you can prove otherwise, please do so with a working example.

  4. Anatoli Papirovski February 2, 2009 @ 11:22 am

    I don’t know why I care about this, but here we go: http://code.fecklessmind.com/print-without-onload/

    Where’s my cookie?

  5. Jeff Starr

    You care because you are so awesome! Thanks for demonstrating the onload-less method.. I see that it does in fact work, and now realize that I was forgetting to call the printClick() function after removing the onload script. You don’t get a cookie, but you do have my appreciation for taking the time to hit me over the head with this — I now have seen the light!! :)

    Edit: The article has been updated to reflect this information.

  6. @Anatoli Papirovski: Your script works well, but it’s not good for performance when we trying to load (and run) script at the end of page. Without onload function, your script will run directly at the place you put it. Of course you can put the function at the bottom, but when other scripts go to large, it will be hard to organize.

  7. Anatoli Papirovski February 3, 2009 @ 9:40 am

    @Tuan Anh: it is very common, in fact recommended, to put JS at the bottom of your files, as it prevents delaying the loading of the rest of the page. I recommend reading “High Performance Web Sites” by Steven Souders.

  8. Jeff Starr

    @Anatoli Papirovski: I think Tuan is pointing out the need for chronological organization of script execution, especially when precise timing is required or when numerous scripts are involved. Utilizing the onload() function is common practice, and greatly facilitates the organization and processing of scripts. Of course, for simple demo pages and other basic JavaScript implementations, using an onload() function may be overkill, as you have demonstrated.

  9. Yes, that’s what I mean. Imaging that you give your code to a newbie, they often put it in the header. Or for another example, when we hook to the WP header (sometimes), script loaded and execute there. It’s not really the best choice for us. The onload method will give us what we want – execute it at last of page, not depend on where we put.

    And of course, your script is good :), in truth, I often do that :D.

  10. Cathy Tibbles October 18, 2009 @ 2:35 pm

    This is the first ‘print this’ post that I’ve tried that actually works with my stylesheet! Thank you. Is there a simple way to change this js to pop up an email window?

  11. Cathy Tibbles October 18, 2009 @ 2:42 pm

    Ack. It only works on the first link. I have full posts on my front page. and I would like to have a print this link on each article. But the js craps out after the first post. Any thoughts?

[ Comments are closed for this post ]