Spring Sale! Save 30% on all books w/ code: PLANET24
Web Dev + WordPress + Security

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.

About the Author
Jeff Starr = Designer. Developer. Producer. Writer. Editor. Etc.
BBQ Pro: The fastest firewall to protect your WordPress.

23 responses to “Unobtrusive JavaScript for ‘Print-This’ Links”

  1. Wow, interesting post! Many sites I’ve seen in the past used to depend on the default javascript function when it comes to printing an article, with a complete disregard of those who have javascript disabled (this problem never caught my attention since back then, I never thought people would want to turn of javascript in their browsers). That’s a nice, nifty script you have over there!

    The dynamic nature of the script is the best part of it, I think – instead of adding the clunky javascript expression into the document itself, you’re adding it by targeting a specific ID assigned to the link, which gives you more flexibility and saves you the trouble of going through every print link manually when a change of code is needed. Sweet!

    Seriously, you never fail to impress me with your God-like speed of churning out efficient scripts. And I like it that you’ve allowed the code to gracefully degrade when javascript is turned off.

    p/s: Just one question – I’m not good at interpreting javascript expressions (I only know the basics because I am just curious of how coders write their arguments in js), may I ask what’s the for loop in the first function for?

    for (var i=0; i < links.length; i++)

    Just curious, heh.

    p/p/s: And I’m wondering if the script works if I place it in the header instead of at the end of the document?

  2. Aalaap Ghag 2009/02/01 9:39 am

    Isn’t this adding a bit of unnecessary overhead? How about just the following:

    Print

  3. Aalaap Ghag 2009/02/01 9:44 am

    Arrgh, WordPress ate my JavaScript. I meant, how about this:

    <a href="#" onclick="try { window.print(); } catch (err) { alert('There is no spoon!'); }" rel="nofollow">Print</a>

    Obtrusive, you say? Then…

    /* print.js */
    function tryPrinting() {
         try {
              window.print();
         } catch (err) {
              alert('There is no spoon!');
              /* put a fancy lightbox / human error message thing here */
         }
    }
    /* end print.js */

    and…

    <a href="#" onclick="tryPrinting();" rel="nofollow">Print</a>

    No?

  4. Oh, silly me. I asked the first question under the assumption that the script will target all the links automatically :P my bad! I guess that’s one of the reason why I never dared to touch javascript or venture into coding… heh. Thanks for the explanation about loading the script in the body – it’s quite true to loading everything in the header might lead to slow loading times if one of the files are faulty or missing. Flickr is one of the examples that causes my site to stall (I’m using their js widget).

    Thanks :) have a great week ahead!

  5. Hi teddY! Thanks for the comment. This script is indeed flexible — I use it here at Perishable for several of my themes and it works so well that I just had to share it with everyone. I am sure there are other scripts available for doing this, but I wasn’t able to find one that was unobtrusive, lightweight, and that didn’t require any JavaScript library. Glad to hear that it may prove useful for you!

    To answer your questions about the script, the “for” code you mention serves as a loop to evaluate all of the links on the page. Any links identified with the associated print_this attribute receive the window.print().

    And, yes, the script works fine when called from any location. In the article, I recommend placing the script link in the footer as per YSlow guidelines, however, linking from the <head> should work perfectly well. I have also updated the article with this information for the benefit of others. Thanks for asking! :)

  6. @Aalaap Ghag: Hmmm.. not sure what you mean here.. The link you provide looks like this in the markup:

    <a href="#" rel="nofollow">Print</a>

    Is that what you had intended to write (sometimes WordPress eats code)? If so, would you care to explain how it works? Perhaps I am missing something here). Thanks!

  7. Aalaap Ghag 2009/02/01 10:54 am

    @Jeff Star: I know, WordPress ate my code and also the follow-up comment with BBCode-style tags! Ugh!

    What I suggested was…

    1) Create a function called tryPrinting that puts a simple window.print() in a try-catch block to prevent errors. The catch block can either show an alert or a fancy lightbox-style error message in case window.print fails.
    2) Put the function in a script tag or in a linked external .js file.
    2) Then have a #print as the a href and an onclick=”trPrinting();”.

    It seemed more elegant, but I thought about this whole thing again and realised that “unobtrusive” is the key word in your post, so what I suggested may not really apply!

    :-P

  8. I think we can make the script better, because we defined ID inside the A element. Remember that inside a document, there is no 2 or more elements have the same ID. So, we don’t need a FOR loop. This is the rewritten script:

    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;
    }

  9. Jeff Starr 2009/02/01 8:31 pm

    @Aalaap Ghag: Rescued that second comment from the clutches of Akismet! Now I see what you mean — that is also a good idea for print-this links, especially for those looking for an even easier, lighter approach. There is no spoon! ;)

  10. Jeff Starr 2009/02/01 8:43 pm

    @Tuan Anh: Yes indeed that is a much cleaner way of doing it. Assuming that the markup is valid and contains only a single instance of the target print_this identifier, there is no reason to loop through the anchor elements. Lighter and faster is always better! Thanks for the reminder! :)

  11. Anatoli Papirovski 2009/02/02 1:53 am

    I’m sorry but a completely unobtrusive “print this” link would be created with JavaScript, rather than hard-coded into the HTML. After all, if I have JS off, then I won’t be able to print the article, so what’s the link for… ?

    Ignoring that, given that your script is at the end of the file, after all the links, it is completely wasteful and unnecessary to include the onload function.

  12. Jeff Starr 2009/02/02 8:27 am

    @Anatoli Papirovski: As mentioned in the article, this technique advises the creation of an alternate “print-this” target page that may provide alternate options for those without JavaScript. Thus, this method is entirely unobtrusive and gracefully degradable, as advertised.

    Ignoring that, your second point is erroneous in that the function will indeed not work without the inclusion of an onload function. You may want to re-read the article, as it clearly instructs the reader to link to an external JavaScript file — not include the actual script itself.

Comments are closed for this post. Something to add? Let me know.
Welcome
Perishable Press is operated by Jeff Starr, a professional web developer and book author with two decades of experience. Here you will find posts about web development, WordPress, security, and more »
Digging Into WordPress: Take your WordPress skills to the next level.
Thoughts
I live right next door to the absolute loudest car in town. And the owner loves to drive it.
8G Firewall now out of beta testing, ready for use on production sites.
It's all about that ad revenue baby.
Note to self: encrypting 500 GB of data on my iMac takes around 8 hours.
Getting back into things after a bit of a break. Currently 7° F outside. Chillz.
2024 is going to make 2020 look like a vacation. Prepare accordingly.
First snow of the year :)
Newsletter
Get news, updates, deals & tips via email.
Email kept private. Easy unsubscribe anytime.