Ask Ben: Print Part Of A Web Page With jQuery

Posted May 21, 2009 at 9:10 PM

Tags: Javascript / DHTML, Ask Ben

Ben, great new look!! Quick question, I am implementing a jQuery modal pop-up in my app and I want the users to be able to print only the content of that modal window. Any idea? Thanks!

To be honest, I've never done this before, so I am not sure if the following solution is truly cross browser compliant. The jQuery plugin that I authored below was tested to be working in FireFox, IE 7, Safari, and the latest version of Chrome Beta (although Chrome seemed to have some issues with the images from time to time). Before we get into how it works, take a look at this demo video so you can see what I'm talking about:

 
 
 
 
 
 
 
 
 
 

As you can see, I created a jQuery plugin to handle the print() functionality. This way, we can make anything on the page printable using a jQuery selector. The caveat being that the containing element is not printed - only it's child elements get added to the print document. In the following page, when the DOM is ready, we find our link element and have it print (upon click) any element on the page with the class "printable:"

 Launch code in new window » Download code as text file »

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>Print Part of a Page With jQuery</title>
  • <script type="text/javascript" src="jquery-1.3.2.js"></script>
  • <script type="text/javascript" src="jquery.print.js"></script>
  • <script type="text/javascript">
  •  
  • // When the document is ready, initialize the link so
  • // that when it is clicked, the printable area of the
  • // page will print.
  • $(
  • function(){
  •  
  • // Hook up the print link.
  • $( "a" )
  • .attr( "href", "javascript:void( 0 )" )
  • .click(
  • function(){
  • // Print the DIV.
  • $( ".printable" ).print();
  •  
  • // Cancel click event.
  • return( false );
  • }
  • )
  • ;
  •  
  • }
  • );
  •  
  • </script>
  •  
  • <style type="text/css">
  •  
  • body {
  • font-family: verdana ;
  • font-size: 14px ;
  • }
  •  
  • h1 {
  • font-size: 180% ;
  • }
  •  
  • h2 {
  • border-bottom: 1px solid #999999 ;
  • }
  •  
  • .printable {
  • border: 1px dotted #CCCCCC ;
  • padding: 10px 10px 10px 10px ;
  • }
  •  
  • img {
  • background-color: #E0E0E0 ;
  • border: 1px solid #666666 ;
  • padding: 5px 5px 5px 5px ;
  • }
  •  
  • a {
  • color: red ;
  • }
  •  
  • </style>
  • </head>
  • <body>
  •  
  • <h1>
  • Print Part of a Page With jQuery
  • </h1>
  •  
  • <p>
  • <a>Print Bio</a>
  • </p>
  •  
  • <div class="printable">
  •  
  • <h2>
  • Jen Rish
  • </h2>
  •  
  • <p>
  • Jen Rish, upcoming fitness and figure model has some
  • crazy developed legs!
  • </p>
  •  
  • <p>
  • <img
  • src="jen_rish_crazy_legs.jpg"
  • width="380"
  • height="570"
  • alt="Jen Rish Has Amazing Legs!"
  • />
  • </p>
  •  
  • <p>
  • I bet she does some <strong>serious squatting</strong>!
  • </p>
  •  
  • </div>
  •  
  • </body>
  • </html>

And, here is the jQuery plugin, print(), that powers this solution:

 Launch code in new window » Download code as text file »

  • // Create a jquery plugin that prints the given element.
  • jQuery.fn.print = function(){
  • // NOTE: We are trimming the jQuery collection down to the
  • // first element in the collection.
  • if (this.size() > 1){
  • this.eq( 0 ).print();
  • return;
  • } else if (!this.size()){
  • return;
  • }
  •  
  • // ASSERT: At this point, we know that the current jQuery
  • // collection (as defined by THIS), contains only one
  • // printable element.
  •  
  • // Create a random name for the print frame.
  • var strFrameName = ("printer-" + (new Date()).getTime());
  •  
  • // Create an iFrame with the new name.
  • var jFrame = $( "<iframe name='" + strFrameName + "'>" );
  •  
  • // Hide the frame (sort of) and attach to the body.
  • jFrame
  • .css( "width", "1px" )
  • .css( "height", "1px" )
  • .css( "position", "absolute" )
  • .css( "left", "-9999px" )
  • .appendTo( $( "body:first" ) )
  • ;
  •  
  • // Get a FRAMES reference to the new frame.
  • var objFrame = window.frames[ strFrameName ];
  •  
  • // Get a reference to the DOM in the new frame.
  • var objDoc = objFrame.document;
  •  
  • // Grab all the style tags and copy to the new
  • // document so that we capture look and feel of
  • // the current document.
  •  
  • // Create a temp document DIV to hold the style tags.
  • // This is the only way I could find to get the style
  • // tags into IE.
  • var jStyleDiv = $( "<div>" ).append(
  • $( "style" ).clone()
  • );
  •  
  • // Write the HTML for the document. In this, we will
  • // write out the HTML of the current element.
  • objDoc.open();
  • objDoc.write( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" );
  • objDoc.write( "<html>" );
  • objDoc.write( "<body>" );
  • objDoc.write( "<head>" );
  • objDoc.write( "<title>" );
  • objDoc.write( document.title );
  • objDoc.write( "</title>" );
  • objDoc.write( jStyleDiv.html() );
  • objDoc.write( "</head>" );
  • objDoc.write( this.html() );
  • objDoc.write( "</body>" );
  • objDoc.write( "</html>" );
  • objDoc.close();
  •  
  • // Print the document.
  • objFrame.focus();
  • objFrame.print();
  •  
  • // Have the frame remove itself in about a minute so that
  • // we don't build up too many of these frames.
  • setTimeout(
  • function(){
  • jFrame.remove();
  • },
  • (60 * 1000)
  • );
  • }

The plugin itself is not too complicated - we are creating an IFrame on the fly, writing the target HTML to its body, and then printing it. It's a simple concept, but there were some issues getting it to work in Internet Explorer (IE). The following caveats tripped me up at first:

  • In IE, you have to focus() the IFrame before you print it other wise the top page prints.
  • In IE, I kept getting errors when trying to use jQuery to write the STYLE tags to the IFrame head. As such, I had to write the html() of the STYLE tags as I was writing out the document. The Style tags are crucial to in the document because they will determine the look and feel of the printed content.

Once I got those two issues out of the way, the rest worked pretty smoothly. I am not sure if this is the best technique, but hopefully it will point you in the right direction.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page




Reader Comments

May 22, 2009 at 1:29 AM // reply »
7 Comments

Ben, do you need the timeout before removing the frame? I did some checking with Safari and FF and found that the code beyond the print() call on the frame does not execute until the user finishes with the print dialog. Haven't had a chance to try this on IE, it's late and I don't feel like starting up my Windows VM :)


May 22, 2009 at 5:34 AM // reply »
18 Comments

That's very clever and makes things so much easier from a developer's perspective.

I used to craft a print.css which makes use of the html media type print and hides all GUI elements on the page among with some other markup changes. It does not work flawlessly with all browsers, though.


May 22, 2009 at 5:48 AM // reply »
177 Comments

I just use jqPrint:
http://plugins.jquery.com/project/jqPrint


May 22, 2009 at 8:06 AM // reply »
6,516 Comments

@Todd,

That plugin looks good. Looking at their code, it is a better solution. I totally forgot about pulling over LINK tags :)

@Ryan,

I just assumed that you would need a timeout. I didn't actually test to see if it was necessary.


May 22, 2009 at 8:13 AM // reply »
177 Comments

I'm sorry, I linked to the wrong plugin that I use. I use this one atm:
http://plugins.jquery.com/project/jPrintArea

Unfortunately, it looks to have been abandoned by the author.


May 22, 2009 at 8:18 AM // reply »
6,516 Comments

@Todd,

Looks to be basically the same. I think they said that the first one you posted was actually an update to the second one you posted. But I could be making that up.


May 22, 2009 at 8:24 AM // reply »
177 Comments

@Ben: Different authors, but improved upon. Yes, you're correct.


May 22, 2009 at 9:15 AM // reply »
6,516 Comments

@Todd,

Looking at the code, I saw "contentWindow". I've never seen that before. Looks really useful - thanks for helping me learn some cool stuff :)


May 22, 2009 at 9:52 AM // reply »
6,516 Comments

@Todd,

Thanks again man:

http://www.bennadel.com/blog/1592-Getting-IFRAME-Window-And-Then-Document-References-With-contentWindow.htm


Jun 17, 2009 at 5:46 PM // reply »
1 Comments

Dude you just saved me a ton of time! Very nice plugin!

Thanks


Jun 19, 2009 at 7:10 PM // reply »
6,516 Comments

@Trung,

No problem my man!


Jun 28, 2009 at 3:17 AM // reply »
1 Comments

Very cool work on this plugin. Just what I needed. But, is there any way to make this work with an input button instead of a text link?


Jun 28, 2009 at 5:41 AM // reply »
34 Comments

I know this isn't related to jQuery but why not just have the printable version load in a coldfusion created PDF? It would eliminate all that code. Just saying...


Jun 29, 2009 at 8:48 AM // reply »
6,516 Comments

@Jody,

It wouldn't really eliminate code as you would have to create a new CFM page for generating the PDF. This page would also have to know how to trim down the requested HTML to just the target section. I think going to a PDF would actually increase the code you would need.

@Vernon,

You just need to bind the click event to the button rather than the link.


Jul 21, 2009 at 7:28 PM // reply »
1 Comments

I can't believe how easy it is to implement.

Thank you so much, you saved me time.


Aug 28, 2009 at 12:52 PM // reply »
1 Comments

dont undesrstend why but when i`m printing in FF - it prints on two pages and when i`m printing in IE7 it printing all except my conent http://v8.dxloo.com/vehicle/2004/VOLKSWAGEN/PASSAT/GLS/WAGON/WVWVD63B14E220600/FOR-SALE-IN/FORKED_RIVER/NJ/200012184/


Sep 2, 2009 at 9:06 AM // reply »
7 Comments

I am... kind of a Mr. Magoo type web designer...

I have a pretty simple question:
In the .printable style, do I have to include any attributes? Does this style HAVE to put in a dotted line or anything at all?

My design is all set up with borders, etc already.

I wonder if I can just enclose my existing divs (the ones I want to include in the print area) in the .printable div, which I do not want to add any formatting to the doc.

Is this clear?
thanks!
finn


Sep 2, 2009 at 9:10 AM // reply »
6,516 Comments

@Finnerty,

The dotted line I added for the demo. You don't need it. Really, just the "printable" class should be enough for the jQuery script to hook into.


Sep 2, 2009 at 9:14 AM // reply »
7 Comments

OK! :-)
on my way to the code, to try it all out. Thank you amigo!!
I'll let you know...

(...looks to his right and clucks... "Oh that Waldo... always worrying!")

fin


Sep 2, 2009 at 9:51 AM // reply »
7 Comments

well... it almost works... I saved "snippet_2.txt" as "jquery.print.js" on my server.

I changed the links to both that and the jquery-1.3.2.js file in the head of my doc, and pasted your Head section parts into my Head, including the "initialize print link" stuff.

I put the .printable style into my CSS, changing the border to "none."

I put the div class="printable" tags enclosing what I want to print.

I enclosed a the words "print this page" between the a and /a tags.

OK!

The link does show the "javascript:void (0) that it should (as per the code in the Head) but.. no print.

I'm thinking I am not hooking up to your script correctly in my Head section, or at least, something is not making the connection to the script to do the work.

That's how it goes over here, if you have the time or inclination, maybe the problem is something really obvious, and I just need a nudge... thanks!!


Sep 2, 2009 at 10:09 AM // reply »
7 Comments

OH YEAH!
You MADE MY WEEK!

I stripped out the <!-- comment at the top of "snippet_2 - jquery.print.js" and...
NICE!!!!!

Thank you amigo!!!
finn :-) :-) :-)


Sep 2, 2009 at 10:17 AM // reply »
7 Comments

UH OH.
Now ALL the links on my page want to do the print thing. That has to be as a result of the function "$( "a" )" I assume.

What can I change that to? Can I add an ID or something to both the function and the tag used to call it?

so close...
finn


Sep 2, 2009 at 10:40 AM // reply »
18 Comments

change $("a") to $(".printme a") and add class="printme" to all <a> elemnts which are supposed to initiate the printing routine. (you can have multiple classes assigned to one html element)


Sep 2, 2009 at 11:28 AM // reply »
7 Comments

OK... done... the links on the page go where they are meant to go again.

But now the print link does not work. It isn't communicating the class attribute I guess...

Not sure what's up, unless the fact that the link is also enclosed in a span class... but that doesn't seem like a problem to me...

< span class="archiveLink" >< a class="printme" >print this page< /a >< /span >

I tried switching the $(".printme a") to $("a .printme")but that was a no-go...

I added the .printme class to my stylesheet, with no attributes... nada...

so... I am just bumbling around...
finn


Sep 2, 2009 at 12:39 PM // reply »
18 Comments

Sorry for confusing you. That was my fault.
You can fix it by moving the class attribute printme to the outer span element.
Like this:
< span class="archiveLink" class="printme" >< a >print this page< /a >< /span >

together with this selector:
$(".printme a")


Sep 2, 2009 at 12:47 PM // reply »
18 Comments

Or you can use $("a.printme") (you nearly got it right!) and have the printme class assigned to the a-element instead.

sorry Ben for flooding your blog comments ;)


Sep 2, 2009 at 12:48 PM // reply »
7 Comments

Happy Happy Joy Joy!!!!

I had to make one little adjustment...

I didn't tell you EVERYTHING...

here's what works:

< div id="archiveLinkDiv" class="printme" >
< span class="archiveLink" >< a >print this page< /a >< /span >    |
< /div >

I had the span enclosed in a div... so the "printme" hook is working off of that..

I TOLD you I was a Mr. Magoo!

You are generous and very kind. Thankyou VERY much!
finn


Sep 6, 2009 at 12:04 PM // reply »
6,516 Comments

@Finnerty,

Glad you got it working - yeah, you have to tell the jQuery selector exactly what link to hook up otherwise, it might hook up all of them!

@Martin,

Thanks for jumping in there to help out!


Sep 11, 2009 at 9:04 AM // reply »
1 Comments

Hi,
is it intended to omit
objDoc.write( "<body>" );

Either way, my original page uses <link> tags to it's stylesheet. You know how to get them?

/HW


Sep 12, 2009 at 10:00 PM // reply »
6,516 Comments

@Henrik,

It should be writing the opening body tag in the above example. I'm not sure why that part is not working for you.

As for LINK tags, those should work the same as with the Style tags. You can probably just swap "style" for "link" in the example.


Sep 26, 2009 at 10:39 AM // reply »
1 Comments

Nice plugin it help a lot... thnx....

Do you have some code on how to change the page setup... like changing portrait to landscape?


Sep 29, 2009 at 9:20 AM // reply »
6,516 Comments

@Eljoe,

I do not know of any way to do that, sorry.


Sep 29, 2009 at 9:36 PM // reply »
1 Comments

thanks for the plugin :)

Everything is working great, except, how do I get rid of the URL printed at the top of the page?

thanks


Oct 1, 2009 at 8:16 AM // reply »
6,516 Comments

@Jeff,

I am not sure there is any way to do that, at least not that I know of; that is a function of the browser's printing mechanism itself.


Oct 15, 2009 at 4:44 AM // reply »
5 Comments

Hi,

Do you know of any way I can hide an image from the printed page, without removing it from the underlying page? I've currently got the following which prints without the image, but (obviously) the image flickers when the print dialogue is displayed:

$( "#LeftContent h1 img").css('display', 'none');
$( "#LeftContent" ).print();
$( "#LeftContent h1 img").css('display', 'block');


Oct 15, 2009 at 4:48 AM // reply »
18 Comments

@Darren you can do this via css. search for css media types.

it goes something like this:

@media print {
/* style sheet for print goes here
it only applies to the printed page*/
}


Oct 15, 2009 at 5:13 AM // reply »
5 Comments

Hi Martin,

I do have a print.css file however I don't believe it's referenced when using the print link. If I use File > Print, it prints as intended, using the css file, but not using the javascript link. Is there something glaring that I'm missing?

Thank you for your reply,

Kind regards,

Darren.


Oct 15, 2009 at 7:30 AM // reply »
18 Comments

Hi Darren, does your "temp document DIV" include the print.css, too? It's used for styling the iframe.

# objDoc.open();
# objDoc.write( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" );
# objDoc.write( "<html>" );
# objDoc.write( "<body>" );
# objDoc.write( "<head>" );
# objDoc.write( "<title>" );
# objDoc.write( document.title );
# objDoc.write( "</title>" );
# objDoc.write( jStyleDiv.html() );
# objDoc.write( "</head>" );
# objDoc.write( this.html() );
# objDoc.write( "</body>" );
# objDoc.write( "</html>" );
# objDoc.close();


Oct 15, 2009 at 7:50 AM // reply »
5 Comments

Hi Martin,

Nope, I wasn't referencing the print css file at all... Turns out I was indeed missing something glaringly obvious!

Thank you very much for your help.

Kind regards,

Darren.


Oct 15, 2009 at 8:02 AM // reply »
6,516 Comments

@Martin, @Darren,

Print style sheets are one of those awesome things that I keep forgetting to take any advantage of when I build stuff.


Oct 15, 2009 at 8:02 AM // reply »
5 Comments

Still can't get it to reference the css file, even though it's now included in the temp div (I've tried absolute, and relative links, different locations in the head for the link, and single then no quotes around the attributes...:

objDoc.open();
objDoc.write( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" );
objDoc.write( "<html>" );
objDoc.write( "<body>" );
objDoc.write( "<head>" );
objDoc.write( "<title>" );
objDoc.write( document.title );
objDoc.write( "</title>" );
objDoc.write( jStyleDiv.html() );
objDoc.write( "<link rel=stylesheet href=/css/print.css type=text/css media=print />" );
objDoc.write( "</head>" );
objDoc.write( this.html() );
objDoc.write( "</body>" );
objDoc.write( "</html>" );
objDoc.close();

Any ideas?

Kind regards,

Darren.


Oct 15, 2009 at 8:16 AM // reply »
18 Comments

Hi Darren, it looks like you're missing the double quotes.

I think this should work:

objDoc.write( "<link rel=\"stylesheet\" href=\"/css/print.css\" type=\"text/css\" media=\"print\" />" );


Oct 15, 2009 at 9:06 AM // reply »
5 Comments

Hi Martin,

Nope, that's not doing it for me either! I got around the image problem by using...

objDoc.write( "<style type=\"text/css\"> img {display:none;}</style> ");

...but haven't been able to reference the css file correctly. Thanks again for all your help though!

Kind regards,

Darren.


Oct 15, 2009 at 11:20 AM // reply »
18 Comments

Hi Darren, nice to read you made it. I've never used the referencing feature myself.
This would be an option as well:
objDoc.write( "<style type=\"text/css\"> @import url(\"./css/print.css\") </style>");

Best Regards
-Martin


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 20, 2009 at 11:32 PM
Five Months Without Hungarian Notation And I'm Loving It
I've used headless camel case for years for not only ColdFusion variables, but also SQL tables and fields... pretty much everything involving code. I also subscribe to the "don't abbreviate and clea ... read »
Nov 20, 2009 at 11:00 PM
Five Months Without Hungarian Notation And I'm Loving It
@Marcel, Yeah, I always err on the side of longer but more readable variable names. As for the camel casing of CF methods and the headless camel casing of custom items, I get around this by always ... read »
Nov 20, 2009 at 10:56 PM
Five Months Without Hungarian Notation And I'm Loving It
I use the following and love it: my.namespace.MyComponents.functionMethodsOrUDF() CONSTANT_VALUES_OR_PROPERTIES One thing I always try is to CamelCaseBuiltInColdFusionFunctions() so others can tell ... read »
Nov 20, 2009 at 5:38 PM
Learning ColdFusion 8: CFImage Part I - Reading And Writing Images
Hi Ben, Great article. I've been looking around to see if ColdFusion image engine can programatically create the following "wrap around" effect: http://www.creativepro.com/article/photoshop-s-she ... read »
Nov 20, 2009 at 5:35 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
@Dave: I talked to Gert he suggested: <cfhttp method="get" url="http://{some cf website}" result="stuff" addtoken="yes" /> Note the addition of cfhttp attribute addtoken. That should persist y ... read »
Nov 20, 2009 at 5:23 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
@Todd, Ahh, gotcha, yeah that makes sense. ... read »
Nov 20, 2009 at 5:17 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
Ben, sorry if I didn't make this clear. You can make it work like that if you want, just put <cfset session.foo = 1> (and <cfset application.foo = 1>) in your OnRequestStart() and it reve ... read »