demosthenes.info

I’m Dudley Storey, the author of Pro CSS3 Animation. This is my blog, where I talk about web design and development with , and . To receive more information, including news, updates, and tips, you should follow me on Twitter or add me on Google+.

web developer guide

my books

Book cover of Pro CSS3 AnimationPro CSS3 Animation, Apress, 2013

my projects

CSSslidy: an auto-generated #RWD image slider. 3.8K of JS, no JQuery. Drop in images, add a line of CSS. Done.

tipster.ioAutomatically provides local tipping customs and percentages for services anywhere.

Photograph of a slice of cheesecake on a plate

Cheesecake

Ingredients

Crust

  • ⅔ cup all-purpose flour
  • 2 tbsp. sugar
  • 2 tbsp. butter
  • 1 tbsp. cold water

Filling

  • 4 cups fat-free cottage cheese
  • 1½ tbsp. sugar
  • ¼ cup all-purpose flour
Photograph of a stack of zucchini brownies on a plate

Zucchini Brownies

Ingredients

  • 2 eggs
  • 1 tbsp. vanilla
  • ¾ cup brown sugar
  • ¼ cup unsweetened applesauce
  • 2 cups shredded zucchini
  • 1 cup whole wheat flour
  • ½ cup dark cocoa powder
  • ¼ tsp. salt
  • 1 cup carob chips

Printing Web Page Components

css / print

Estimated reading time: 4 minutes

Last month my Print Stylesheets Tips & Tricks article on Smashing Magazine received an interesting question: what if a user wants to print individually separate sections of a web page?

The questioner gave a good example: a page that presents multiple recipes (those used in the example above supplied by my student Jenelle Bucholz, from her recent 1st year site project). The for the recipes would look something like this:

<article id=article1>
<h1>Light New York Cheesecake</h1>
<h2>Ingredients</h2>
<h3>Crust</h3>
<ul>
<li>⅔ cup all-purpose flour
</article>
<article id=article2>
<h1>Zucchini Brownies</h1>
<h2>Ingredients</h2>
<ul>
<li>2 eggs
<li>1 tbsp. vanilla
</article>

And the default like this:

body {
display: flex;
font-family: Edel Sans, Arial, sans-serif;
}
article {
margin: 0; position: relative; padding-bottom: 2rem;
}
article img { width: 100%; height: auto; }
article h1 { line-height: 1.4; text-align: center; }
article h2 {
font-size: 1.4rem;
border-bottom: 2px solid rgb(133,183,174);
margin: 1rem auto; padding: .3rem;
width: 8rem; text-align:  center;
font-weight: 200;
}
article ul, article ol { font-size: 1.2rem; line-height: 1.6; }
article ul, article ol { padding-left: 0; }
article ol { padding-left: 1rem; }
article ul { list-style-type: none;  }
article button {
position: absolute;
bottom: 0; right: 1rem;
padding: .5rem;
}

(I’m using for convenience, but there are plenty of alternative ways to place the recipes side-by-side).

Creating Print Buttons

The buttons are inserted at the bottom of each article:

<button>Print recipe</button>
</article>

It’s possible to make the each button a simple print action at this stage:

<button onclick="window.print()">Print recipe</button>

Written that way, the buttons will print out the entire page, but our goal is more ambitious that this. Because I want to keep the code as clean as possible, I’ll do all of the work in JavaScript at the end of the document, rather than inline for each element.

Allowing For Printing Choices

A visitor will probably want to print out a single recipe for the sake of convenience. In this case, I assume that the site owner wants page components such as <header> elements and comments to print out when the user clicks Print, but no other articles aside from the one associated with the button. As a start, I’ll add the following to the end of the document:

<script>
function printSection() {
var parentArticle = this.parentNode,
articles = document.getElementsByTagName("article");
for (var i = 0; i < articles.length; i++) {
if (articles[i] !== parentArticle) articles[i].style.display = "none";
}
window.print();
}
var printRequestors = document.querySelectorAll("article button:last-of-type");
for (var i = 0; i < printRequestors.length; i++) {
printRequestors[i].addEventListener("click", printSection, false);
}
</script>

Very simply, the script attaches a click event to the last button in each article (we’re assuming that’s the Print button, although we could also identify the buttons with a class to be on the safe side). When a button is clicked, it calls the printSection function, which:

  • Determines the parent article of that particular button.
  • Turns off the display of all other articles.
  • Prints the resulting page.

This works, but it leaves the browser window looking like the page that has just printed, i.e. with the other recipes removed. In reality, we want the display: none rule to affect articles only during print, not on the screen. Also, we don’t want the Print button itself to be visible on the printed page.

The last part is easy to do, via adding a media query to our main stylesheet:

@media print { article button:last-of-type { display: none; } }

The other goal is a little trickier: unfortunately, we can’t just command JavaScript with  parentArticle.print();. But we can create a new style sheet at the end of our document that contains added rules for print. The JavaScript changes to:

function removePrintStyles() {
previousExclusions = document.getElementById("printexclusions");
if (previousExclusions) previousExclusions.parentNode.removeChild(previousExclusions);
}
function printSection() {
var css = document.createElement("style"),
parentArticle = this.parentNode,
articles = document.getElementsByTagName("article");
css.id = "printexclusions";
css.innerHTML += "@media print {\n"
for (var i = 0; i < articles.length; i++) {
if (articles[i] !== parentArticle) { css.innerHTML += "#"+articles[i].id + "{ display: none; }\n"; }
}
css.innerHTML += "}\n"
removePrintStyles();
document.body.appendChild(css);
window.print();
removePrintStyles();
}
var printRequestors = document.querySelectorAll("article button:last-of-type");
for (var i = 0; i < printRequestors.length; i++) {
printRequestors[i].addEventListener("click", printSection, false);
}

In short, the rewritten script creates a new print stylesheet at the bottom of the document, eliminating the presentation of any other recipes when the Print button is clicked; thereafter, if the button for another recipe is clicked, the previous stylesheet is removed and rewritten. The stylesheet is removed after any individual print action, as otherwise it will influence the printing of the entire page, if the user decides to take that route.

Improvements

There are several assumptions in the script: that every <article> element on the page has a unique id; that visitors are using a modern browser (due to the use of document.querySelectorAll and addEventListener), and that itself is working. These may not be reasonable assumptions in all cases, but each could be addressed if they were felt to be an issue. Inspect the code for the page print component

comments powered by Disqus

This site helps millions of visitors while remaining ad-free. For less than the price of a cup of coffee, you can help pay for bandwidth and server costs while encouraging further articles.