This approach works fine for content assembled on the server — the web application generates the HTML markup in it's final form before sending it to the browser. Likewise, when the user hits the print version button, they're either going to see the same exact page with different styles applied to it, or be redirected to another URI — /page/print/ for instance. It's difficult, however, to make use of this approach when we've got dynamic content being pushed from the server onto the page. How can we format the page for printing in a universal way when the content doesn't have a strict URI associated with it?
Dynamic content and URIs
When we're talking about dynamic content in web applications, we're not talking about the application server that fills in the empty template slots. True, this is dynamic, but not from the user's perspective. In this sense, the user is associating the content with a single URI — a one-to-one mapping. The URI is the reader's key to fetching a chunk of content.
For written content, news sites and bogs for example, this approach works well because you can share URIs. For applications, this is a little trickier — especially for applications that follow more of an Ajaxy paradigm and aren't necessarily URI bound. In these types of environments, the user is is more concerned with generated output — tables and other forms of information that convey the state of the application.
But the application needs to get this data from somewhere — the Javascript Ajax calls need to follow some path to the application that'll return something of value for the user interface to display. This is usually an API that returns JSON data that the user interface library, jQuery for example, knows how to parse and understand. The data these URIs return are perfect for user interfaces, unfitting for user looking to print that data.
Although some — perhaps most — of this application-style content isn't suitable for printing. But some of it probably is. A grid would be a good example of this. Grids take on a specific look and feel inside the web application user interface. Suppose they want to take a look at it on the train ride home in dead-tree format. Is the grid widget optimized for printing?
Building new documents
We could, to help solve the problem of printing this grid widget, implement functionality that'll take any dynamic data rows, and the grid as a whole, and adjust it for printing. That's fine for this particular use-case. But it also makes two assumptions that are at best sub-optimal. First, if our application uses a grid that is populated dynamically via an API, chances are we're going to have other printable content. So this means any printing strategy we employ is going to need to support more than just the grid. Anything less is prohibitive in value.
The second problem, perhaps not so obvious as we're used to printing static web content, is how does the user return to where they were in the application after printing? We could always apply some style changes that'll make the content printable in the current browser window. This introduces more complexity, however, because we've now got to roll back those changes once the user has printed their document.
Another approach, one that I think behaves more intuitively, is to have the user interface identify printable regions in the interface when they click the print button. These regions are then copied over to a new document before being formatted to print. This is like following a print friendly URI for static content — it doesn't change the original browser window.
Here is some basic markup. Let's pretend that the content div has been inserted into the document as a result of an Ajax call...
<html>
<body>
<div class='content'>
Some <a href="#">content</a> with <a href="#">links</a> inside.
</div>
<button class='print'>Print</button>
</body>
</html>
We've also got a print button in our user interface. Let's implement some example printing functionality for Ajax content...
$(document).ready(function(){
function replace(){$(this).after($(this).text()).remove()}
$('button.print').live('click', function(event){
var print_window = window.open(),
print_document = $('div.content').clone();
print_document.find('a')
.each(replace);
print_window.document.open();
print_window.document.write(print_document.html());
print_window.document.close();
print_window.print();
print_window.close();
});
});
And that's it. This is a simple example of how we can locate and modify information inserted by Ajax API calls and create a new document suitable for printing.
All this example does is replace a elements with their text. But before doing so, the div.content element is cloned — so as not to disrupt the user interface. Finally, we can copy the new modified element into a new browser window for printing. We're automatically going to print this for the user, and close the new browser window to make the whole experience as unobtrusive as possible.