The “blacklist” is a common approach to print stylesheets. We know that people probably don’t need to see our site navigation if they print out an article on our site. So we hide it from print like we would hide it from the screen (display: none;
).
Is there a way to reverse that?
The Blacklist Technique
You select all the elements on the page you don’t want printed, and put that into some CSS that is applied to the print media. Perhaps in a block at the bottom of your main stylesheet.
@media print {
.main-navigation, .comments, .sidebar, .footer {
display: none;
}
}
Alternatively, you could leave the job mostly the the HTML. You could create a “don’t print” class and apply it as needed.
@media print {
.dont-print {
display: none;
}
}
<section id="comments" class="comments dont-print">
</section>
Blacklisting is a common tactic, posted in print stylesheet tip articles all around the internet.
The only trouble with it is that it requires maintenance. HTML changes, so you’ll need to maintain either your list of selectors to not print, or the classes in the HTML to be on the right elements. Easy to forget.
The Whitelist Technique
Whitelisting would be the opposite technique. In your print styles, everything would be hidden from print except for elements you explicitly choose.
Don’t get your hopes up too much though, it’s pretty tricky to pull off. My first thought was to universally hide things, then override with a class.
/* Bad idea #1 */
@media print {
* {
display: none;
}
.print-me {
display: block;
}
}
<main class="main-content print-me">
</main>
There are two problems here:
- The
display
property isn’t inherited, so even though you told an element to show itself again, it’s child elements will still be hidden by the universal selector selecting and hiding them. - The parent elements of the element you told to show itself may still be hidden.
The latter of which we could maybe solve with…
/* Bad idea #2 */
@media print {
* {
display: none;
}
.print-me,
.print-me * {
display: block;
}
}
… selecting the child elements and having them show themselves again. But that would make all child elements block-level, which is bad. You don’t want your <a>
, <em>
, <strong>
‘s and anything else that is inline
, inline-block
, inline-table
, inline-flex
, etc to become a block.
That would be harder to maintain than a blacklist.
I considered manually requiring all parent elements to also have “print-me” class, but that will expose sibling elements to being printed when they shouldn’t be (don’t have the class). I tried fixing that with a
/* Bad idea #3 (addition to previous) */
.print-me ~ *:not(.print-me) {
display: none;
}
But that doesn’t handle previous siblings.
This could be a pretty good use case for parent selectors in CSS, since you could potentially use a parent selector to un-hide a parent element if it contained an element with that specific class name. Theoretically something like *:contains(.print-me) { display: block; }
.
There may be a pure CSS solution to this yet. I admittedly didn’t spend hours and hours on this. There may be a clever tactic here I’m missing. I know that the visibility property doesn’t inherit, so there may be potential there, but that doesn’t effect layout like you may want it to. I’m not sure :not()
was explored to its fullest potential here either.
If you want to play with ideas, here’s a test page for you. It just has a button that toggles a class on the parent of a bunch of content, so you can pretend that class is like print styles and get it to do what you want.
My closest attempt so far is a just give up and use JavaScript attempt. Using jQuery here for easy DOM traversal, the crux of it is automatically applying a class to all parents of an element ensuring they can be printed:
$(".print-me")
.parents()
.addClass("js-print-me");
Note I’m using a slightly different class name, so it’s not affected by the same rule that selects all descendants to display.
@media print {
* {
display: none;
}
.print-me,
.print-me * {
display: block;
}
.js-print-me {
display: block;
}
}
There are even sorta-kinda ways to detect a print event, so you could apply/remove a parent class name to get the page in this state when a user goes to print.
This doesn’t solve the inline-* problem though (where they go block-level instead of remaining what display value they used to be) which makes it fairly unusable. If you find JavaScript is an acceptable choice here, you could move the job of hiding to JavaScript as well, only applying display: none;
after saving what display type it already was, so you could put it back when done. I think jQuery even kinda does this already somehow?
Rumor has it there is some future CSS something-or-other that can handling toggling of visibility in a way that doesn’t tie it to layout (and that this is a pretty good use-case).
I was just fiddling with these techniques yesterday at work! Thanks for clearing a few things up for me. Great article.
Not quite sure of a real world example for this and I’ve personally never come across an issue where i need to switch print on. Does anyone create ‘print first’ CSS?
Using html5 spec using
<nav>
one would usually switch out that block by default (interactive menus being defunct in print), and make anchor link adjustments and not much else if I can help it!I was fiddling with this stuff the other day as well following your tweet. I came to a similar conclusion using JavaScript, but just looped over elements with the
print-me
class and appended them to thebody
. That way nesting is irrelevant. Check it: http://codepen.io/hkfoster/pen/LVmwWoAs mentioned, I don’t really have a use case for this, but it was an interesting exercise nonetheless.
.print-me * {display: block;}
is a bad idea, imho. Maybe there are inline-descendants.Right! That’s why I said:
Hopefully some future CSS can save us with a true single-responsibility hiding toggle.
Actually, it doesn’t have to be so messy.
You just
visibility: hidden
the html element in print stylesheet, and then just create aprintable
class withvisibility: visible
and apply it selectively (efectively a whitelisting technique).This works because
visibility
in CSS can be overriden in child nodes, whiledisplay: none
discards the whole subtree from the display list.When you print the page, you’ll get a blank space instead of hidden elements.
Right! In the article:
The classic example is likely a sidebar. You don’t want it just to be blank, you want it to collapse to nothing so the main content area can expand on the printed page.
@Chris: In the demo, the way you have the
.print-me
class on a child of a floated item instead of the floated item itself, there’s no way for it to ‘break out’ and be full width. You could try to usewidth: 100vw
, but that could get messy.Thank you for this useful tutorial.
How about setting display: initial;, as discussed on CSS tricks a while ago? When applied to the .print-me,.print-me * {} selector, it should make all child elements inherit their original state right?
Jelle
No,
display: initial;
would produce exactly the same result asdisplay: block;
(the initial value for the display property is block).Interesting, there is a proposal in CSS Selectors Level 4 for a keyword that would work like you want it to. It’s currently called
default
(although that’s likely to change), and it would roll back the declaration to the user-agent stylesheet (or the user stylesheet, if one exists). Since UA stylesheets tend to set things likep{display:block;}
andspan{display:inline;}
, all you’d need would be.print-me,.print-me * {display:default;}
.Ugh, I hate it when you notice errors after you submit a comment. I meant CSS Cascading and Inheritance Level 4, of course!
At least I got the link right…
Breaking news! The keyword formerly known as
default
is nowrevert
.There are other possibilities that display: none to hide content – we just need to find one that is easily reversible to its previous state.
Ooo that brings up an interesting point. Yes we need a better hiding toggle. But maybe one of those alternative hiding methods could help us in the short term, like
clip()
or position-absolute-kick-off-page.You can’t use the print event because it’s fired pretty fast. The browser don’t have time to apply the javascript class changes and render the print result properly. The browser also don’t allow you to delay the print event when it’s fired from the browser interface because it can be perceived as a browser’s bug.
The print rendering is buggy in some browsers even when you need to use a responsible print stylesheet. Sometimes, if you try to change the font-family, font-size or do an image replacement for print it don’t works because the render is pretty fast.
Print media styling should be done this way:
For the sake of our planet! That’s the way I do it on my website because what’s online doesn’t have to be on paper. Want to keep it? Set a bookmark. Easy as that.
Probably you are the guy that disable right click and ctrl+C.
No, that doesn’t save our planet.
Here’s my best attempt: http://codepen.io/shshaw/pen/QbxObX
First, everything is set to
visibility: hidden
except.print-me
and its children so at the very least, the undesired elements won’t appear.Then direct descendants that are not
.print-me
are set to no height, margin or padding. If they contain a.print-me
item, thedisplay: table
ensures that it expands to fit.width: 100%
is optional, but helps items take up the full width.Setting
font-size: 0
ensures that it completely collapses, with the size being reset with.print-me { font-size: initial; }
No idea about browser support here, but it seems like a good start.
It’s a good idea. Pretty hacky, but if can be evolved to work like a css-reset to any website.
The problem in you case is that the sidebar even invisible, still is occupying real state. If it’s applied to a more complex layout (or even if the sidebar was on left), you can end with some empty areas in your page.
You can fix it though, but then you will have to fix paddings and probably will have to fix individual elements.
@Kyrodes The biggest issue with the sidebar in the example that Chris made is the
.print-me
class is on a child of a floated element, not the floated element itself. In order to make that child full-width, you’d really get into hack territory with settingwidth: 100vw
, potentially with negative margins.If the
.print-me
class was on the floated element, you could simply add.print-me { float: none; width: 100%; }
and be done. I’ve updated the demo to illustrate that.There’s also
screen
media type which is not used while you’re printing, so you can enable certain elements only for screen:To make it compatible with old browsers you could probably wrap
display:none
inside ofonly all
:This is what I do for Shower themes: I just wrap all code that I don’t need in print into
screen
:+1, this is the cleanest solution (the code describes itself perfectly and doesn’t require any documentation really) and works everywhere I’ve ever tested it. Note that whilst some devices/platforms have implemented other non-print @media types such as “handheld” and “projector”, AFAIK none of them have done so without also resolving true for “screen” (for just this sort of reason, I imagine).
PS, you don’t need to do anything like your second code block to support older browsers – basic @media types were implemented everywhere long before media queries.
I’m pretty sure
handheld
andprojector
are deprecated. Current onlyall
,screen
,print
andspeech
are allowed.IE8 don’t support
@media
just:<link rel="stylesheet" media="print" href="/print.css" type="text/css" />
How about adding a “width: initial;” to “.fake-print-state *”?
Block elements will take up the full width with that, unless I’m mistaken.
You could even remove the float if you like.
here’s an example: http://codepen.io/TheDutchCoder/pen/GJGQLd
I would recommend looking at jxnblk‘s ReadRead.
On a site I designed, I used the blacklist method. However, there were certain elements on the page that I wanted to show up only if it were printed. I had a .noprint class and a .printonly class. Worked rather nicely.
You could also not worry about applying the actual
.print-me
class itself to any of markup and just use a pre-processor to extend it where useful.Like so (in LESS):
By extending the class instead of stuffing it in all over the markup, you get to side-step a lot of the above issues.
You can use this strategy to do all kinds of cool things.
Isn’t using the universal selector as a descendant selector bad for performance and considered bad practice? I.e. to see if
.print-me *
should apply we would have to traverse the DOM for every element to see if any of its parents have the.print-me
class.Here’s a CSS-only whitelist approach. The keys are: (1) use visibility; (2) move the print section to the top-left.
See my Stack overflow answer for (a little) more explanation.
I dig that!
But it does limit the “whitelist” to a list of one. Hence the ID used in the demo I guess.
Robert Stewart writes in with a plugin for an alternative approach:
https://github.com/erikzaadi/jQueryPlugins/tree/master/jQuery.printElement/Sample