How to create an unobtrusive print this page link with JavaScript

When a client requests that I duplicate functionality that should be (and is) handled by web browsers, I always try to avoid doing it by explaining why I believe it is better to leave such functionality to the browser. Most of the time I succed, but occasionally I don’t.

One common request that duplicates browser functionality is a “Print this page” link. Most developers have probably added such links more than once during their career. Doing so isn’t necessarily “wrong”, and it won’t bring serious harm to any kittens or other cute animals, but it is very unusual to see print links implemented in an unobtrusive way, so here is how to do that.

First a look at the obtrusive way. Most of the time print links are hardcoded in the HTML, and look something like this:

  1. <a href="javascript:window.print()">Print this page</a>

While that does work for most users, it doesn’t separate content from behaviour. It also uses the fake javascript: protocol. But worst of all, it will confuse people browsing without JavaScript. The link will still be there, but when they click it, nothing happens.

The simple solution to this problem is to use JavaScript to create the link (or button, which arguably may be a more suitable element). The following script takes an element id and the link text as arguments, creates a link with an onclick event handler that triggers the browser’s print function, and appends it to the element:

  1. var addPrintLink = {
  2. init:function(sTargetEl,sLinkText) {
  3. if (!document.getElementById || !document.createTextNode) {return;} // Check for DOM support
  4. if (!document.getElementById(sTargetEl)) {return;} // Check that the target element actually exists
  5. if (!window.print) {return;} // Make sure the browser supports window.print
  6. var oTarget = document.getElementById(sTargetEl);
  7. var oLink = document.createElement('a');
  8. oLink.id = 'print-link'; // Give the link an id to allow styling
  9. oLink.href = '#'; // Make the link focusable for keyboard users
  10. oLink.appendChild(document.createTextNode(sLinkText));
  11. oLink.onclick = function() {window.print(); return false;} // Return false prevents the browser from following the link and jumping to the top of the page after printing
  12. oTarget.appendChild(oLink);
  13. }
  14. /* addEvent function removed from this listing*/
  15. };
  16. addPrintLink.addEvent(window, 'load', function(){addPrintLink.init('article','Print this page');});

The full script is available in addprintlink.js, and I made a simple demo page where you can compare the obtrusive and unobtrusive ways of creating print links.

I have tested the script in every browser I have access to, and it works as expected everywhere with three exceptions, all related to Apple WebKit. In OmniWeb, Shiira, and Safari 3 beta for Windows, window.print evaluates to true, but nothing happens when window.print() is executed. window.print() being broken is listed as a known issue on the OmniWeb support page, and there is a note about the problem in the Windows version of Safari 3 on Apple’s support site, but I can’t find anything about it in the Shiira forum. It seems likely that it will eventually be fixed in all three browsers.

Browser bugs aside, the take-home lesson from this article is that any links or other elements that require JavaScript to work properly should also be created with JavaScript. Otherwise you risk confusing and annoying users who are browsing without JavaScript support.

Posted on September 14, 2007 in JavaScript, Usability

Comments

  1. I think that “print this page” links have merit when they restyle the page in a “printer-friendly way”. Sure, us developers understand that print-styles can be automatically applied when printing, but the general user probably doesn’t understand this.

  2. September 14, 2007 by zcorpan

    I’ll play the devil’s advocate for a bit… :-)

    The following code should be equally unobstrusive (although it wouldn’t work in XHTML, but your function doesn’t either since createElement() won’t (or shouldn’t) create an XHTML element):

    document.write('<a href="javascript:window.print()">Print this page<\/a>');
    

    Using innerHTML instead should make it possible to move the script outside the body and should also work in XHTML.

  3. I agree with Tim - it’s what I do for lots of work projects. I re-iterate my call from last summer’s geek in the park that browser manufacturers have a nice glowing print icon if a print stylesheet is found.

    By the way, I do my print this page links the dirty way; a bit of JavaScript does a document.write of the link. But that’s cause I’m rubbish at JavaScript….

  4. September 14, 2007 by MarkeeO

    It’s a bit of an irony that this post deals on an unobtrusive way to creak a “Print This Page” link when most of the people who implement such a script are the one whose pages aren’t even styled for printing.. :D

    Personally, I prefer a “Printer-Friendly Version” link which either takes me to a printer friendly copy of the page or dynamically switches the stylesheet so everything really becomes printer friendly.

    I guess the best thing would be a “Print This Page” link (or button) that would automatically change switch into a print stylesheet and then use the window.print() script to actually start printing the page. The merits of this technique is the fact that even if javascript isn’t enabled (assuming you use another technique other that jscript for stylesheet switching), the visual feedback of the page turning into a printer friendly page would compensate for the automation of calling print from the menu.

  5. September 14, 2007 by Roger Johansson (Author comment)

    Tim, Bruce: Yes, displaying a print-formatted version of the page does give a “print this page” link more merit.

    zcorpan: Yeah, document.write() would also work (though as you say not in XHTML served as application/xhtml+xml). DOM scripting is neater though ;-).

    MarkeeO: I guess that is a bit ironic ;-).

  6. I do this, but also extend the same technique to encompass the ‘Back to previous page’ links my designers seem to like adding.

  7. If I would do what you have done I would first make a link in the html-code to a print-preview/printer friendly version of the page.

    <a href=”example.com/same-page/print/” rel=”print-preview”>Print preview this</a>

    I would then find all print-preview links and attach a click-event to them which will change the current page’s stylesheet to it’s printer-stylesheet as well as replacing the link with a link that restores the page and if supported I adding a new link for printing the page, much like you’re describing.

    I would do that because I don’t want to print anything without knowing how it will look. Many pages look terrible in print - some can’t even be read properly.

    If you’re showing me what I get before you’re suggesting that I should print it I’m probably more likely to do what you’re suggesting.

  8. MarkeeO, why not just use a different stylesheet for print media that hides the navbar classes? No need for a print link at all (unless there is some reason your visitors might want to print your navbars).

  9. Good stuff. Bookmarked. I don’t do much with JavaScript, but have been recently reading up on some of the newer concepts (unobtrusive, dom, jquery, etc), and JavaScript in this way seems to be very usable, and doesn’t break the page when disabled.

    I think I know a few places I’ll be able to use this functionality.

    A question though: The addEvent function call (last line) seems to add a handler to the window’s onload event, so why attach that to the addPrintLink var?

  10. Another method I’ve used is setting display:none on the element that requires javascript. Then I just put a little script later in the code to display it. If a user doesn’t have javascript, they never see the element.

    Doing it this way does avoid having to dynamically create elements in the DOM, but I don’t know if that’s necessarily a good thing in this case.

    It also brings up an interesting question about screen readers. I believe most screen readers currently skip over display:none elements, so I’m not exactly sure how this would act. Any thoughts on this?

  11. September 14, 2007 by Roger Johansson (Author comment)

    Pelle: I’m not suggesting you add “print this page” links, only that if you must, do it in an unobtrusive way.

    Grant: The code in this article is not complete. The full script contains an addEvent() function which is called in the last line.

  12. Ah I see, the addEvent function is part of the addPrintLink var…I had thought it was a standalone function.

    Which brings up anther question: why make addEvent a function of addPrintLink alone? Why not have addEvent as a standalone function, able to be used in any other situation?

  13. I am wondering the same thing as Grant. I noticed this in one of your other JS examples (The pullquote I believe). Wouldn’t it be best to keep the AddEvent outside of the print function (and the pullquote) - then it could be used for both. You would have to repeat it continually, or call AddEvent from the scope of an unrelated object.

    Thoughts?

  14. Grant, Nate: This is just a guess why Roger did it that way, but here it goes.

    It’s usually a good idea to put all the functionality under one global object. Especially when writing scripts that others might use on their sites. That way you minimize the possibility of a conflict in the global namespace. There’s a chance someone putting this code on their site might already have a global function named ‘AddEvent’.

  15. September 14, 2007 by Roger Johansson (Author comment)

    Eric: Using display:none to hide the link would cause confusion for people with CSS and JavaScript off though. I definitely think anything that requires JavaScript should be written to the page with JavaScript as well to avoid that.

    Grant, Nate: I included the addEvent function inside the main function to make the script work as a standalone without interfering with any other addEvent functions. There is a fairly visible comment in there that encourages you to replace it with whatever addEvent function, if any, you are already using ;-).

    Edit: Oops, I see Eric answered the addEvent question while I was writing my comment :-).

  16. God bless the print link and all that sail upon her. On a more serious note; this is a great idea for those projects where the board member in charge of the budget says we need to add a print link because he couldn’t see how to print it all (usually because there’s a post-it on that part of the screen telling him to play tetris)

    I do though see a point to the print link functionality within the context of a site. Even though, as we all know, there’s a huge printer icon in the browser.

    For example, in the context of a booking journey within a travel site, it is often required that the user makes a copy of booking information post-payment such as the booking reference number etc. In the context of informing the user that they should make a copy of this information it’s (in my mind) more user friendly to include this link within that area of the page, possibly lowering the amount of mouse movement required.

    That and when we tied up a bunch of people and led them in for some user testing on my employers “new web-platform development” the less net savvy liked the ability to click on that part of the page to print without having to know how to use the browser. (yes we also email the info to the registered email address) Now when we add on top of this the amount of not quite as web savy, people installing Firefox we have a whole range of people who don’t have the big print icon on their menu by default (correct me if I’m wrong, I’ve not seen the default menu since phoenix, and am basing this on screen-shots off life hacker [quote[http://lifehacker.com/software/top/firefox-20-beta-1-screenshots-186687.php]]). Surely anything we can do to make a users life easier is worth doing? Anyway rant over, once again great article. Two thumbs fresh :o)

  17. Your example somewhat resembles this solution I made a while back - though mine also allows the user to print preview…

    Most people are apparently confused when the style suddenly changes upon print, so I thought adding a print preview would be a nice way to go…

    http://users.skumleren.net/cers/examples/printpreview.html

    pay no attention to the shoddy design - it was merely a test…

  18. for the webkit stuff, or any browser, the better test is:

    if(window.print && typeof(window.print) == ‘function’){ //do your thang! }

    this will bail out, if window.print doesn’t exist, then it will bail out if window.print isn’t a function.

  19. September 14, 2007 by Roger Johansson (Author comment)

    jerry:

    this will bail out, if window.print doesn’t exist, then it will bail out if window.print isn’t a function.

    Unfortunately the WebKit browsers (Ok I only checked your suggestion in OmniWeb and assume the same will be true for the others) return “function” as the type for window.print. Good thought though.

  20. I did a similar thing for a client recently but I took a little different road. I started out with a regular link to a page where I described how the user can print the page without JS in all sorts of browsers.

    <a href=”http://www.example.com/about/#howtoprint” class=”print”>How to print this page.</a>

    If the browser supports JS then I just add an onclick-event similar to yours and changed the text for all “a.print” using JQuery (Man is that a good library).

    I think that’s a good way because then the user without JS gets an explanation how to print the page.

  21. I make it also as icaaq and find these away personally best

  22. Having gone the route of “Print version” in the old days, then proudly adding a print style sheet, both leave me wanting. Most of the time I and those coming to a site just want to print the article, (not the menus, etc.), but sometimes we want to print the page as seen. With a print style sheet, that leaves the viewer relying on screen dumps and an image manipulator; more than most want to do. Now I’m considering dual “print article” and “print page” links. Too fussy?

  23. I’ve been wondering about this lately too. Not so much the unobtrusive implementation (though of course I’d do that, heh), but the comment CW Petersen just made.

    I have a client asking whether print links are good or bad from a usability point of view. My take is that they’re probably good as it helps those users who are unfamiliar with the browser print functionality. The question I have is around print style sheets, print friendly pages or nothing at all.

    I always use print style sheets, but I heard someone mention recently that this can confuse users who expected to print exactly what they saw on the screen. An alternative would therefore be the print-friendly version which the user gets to see (in another window or pop-up) before printing to give them an idea of what the print-out is going to look like before they send it to their printer. Is this considered good practice though?

    What do you think is the best approach to this?

  24. The code you came up with is definitely useful, but I personally prefer a the variation similar to what Christian presented.

    I’m not much of a ‘printer’ so to speak. If I see a link to a ‘printer friendly page’ I tend to click and save the page. To be honest, I can’t remember the last time I printed something off a site I was browsing on the web. I may very well be a small minority, but more and more sites are presenting ‘printer friendly’ versions versus triggering the printer and that’s getting to be what I expect when I see a print icon. Is it starting to change meaning or is it just me? Who knows. But 99% of the time I don’t even have my printer turned on, so a print link that tries to open a dialog for that device is useless (at best) and annoying (at worst) to me.

    In any case, I like your script, but I like the idea of implementing it AFTER showing a printer friendly version (as Christian showed). Giving people the option to do what they want with it. It does add another ‘step’ to the user process, but it also avoids an assumption that there is a device connected, as well as avoiding the confusion over whether its a ‘printable version’ or a ‘print to device’ - which I wish more sites would clearly label.

  25. Can you control where the link is displayed? Is it always at the bottom of the page?

  26. I’m not much a printer either, but I like your script. Simple, to the point. I use a custom script that I’ve built but it accomplishes the same thing. Good stuff.

  27. I use the document.write method to put the link on the page when I do this, but I’m wrong in doing so and know it… I’m just JavaScript-stupid. So, thanks for this, Roger. I will implement your method in the future.

  28. Eh.. what’s wrong with the ‘print-button’ on the browser’s toolbar? Anyone ever used that?

    You might even consider an extra line of text: “Press Ctrl-P to print this page”. That might do the trick w/o any coding ;)

  29. September 19, 2007 by Roger Johansson (Author comment)

    EC:

    Eh.. what’s wrong with the ‘print-button’ on the browser’s toolbar? Anyone ever used that?

    Nothing wrong with it (or the File - Print menu option, or the keyboard shortcut). Sometimes clients refuse to accept that though.

  30. what a timely post. i just had a client ask for a “print this page” icon to be placed on the page. this definitely does the trick. thanks!

    p.s. ever thought of having a “back to top” link down here somewhere? i would so click on that :o]

  31. September 20, 2007 by Brian LePore

    Dania:

    This function takes an id as the first parameter to the function, which is where the link is displayed. Yes, appendChild will make it the last child of any element if there are multiple children of an element, but you could make the id you give it an empty element (i.e. one with no children). For example, one could place the following anywhere in there page:

    <div id=”empty”></div>

    And then the addEvent function would be:

    addPrintLink.addEvent(window, ‘load’, function(){addPrintLink.init(‘empty’,’Print this page’);});

  32. wonderful! .. thank you!

  33. Is there a way to include a small image (icon) with (right next to) the “print this page” text?

  34. Nice implementation, I would also suggest to add a print stylesheet, and replace current styleheet with the print stylesheet.

  35. October 17, 2007 by Steve

    If anyone is so bloody stupid that they are not able to use a browser to print - then maybe they don’t deserve to use a computer.

  36. Steve: I hope you don’t actually apply that philosophy to your development

    Roger: Thanks for this- I’ve been developing with unobtrusive JavaScript for a while now but print links are something I had never taken the time to work out like you have here. This technique will be great for future projects :-)

    Regarding the print link discussion… I like to place print links on pages that visitors would want to print (news articles, support articles, etc). This lets them know that they’re on a page that may be beneficial for them to print, and that maybe it will even look nice when it comes off their inkjet (or laserjet).

  37. This is a nice article Mr. Johanson. Unfortunately here in Greece there are so many HTML authors and developers that donot understand a bit of what unobtrusive Javascript means. They asume that every user will have Javascript enabled!

Comments are disabled for this post (read why), but if you have spotted an error or have additional info that you think should be in this post, feel free to contact me.