Recently I stumbled across an article on zOompf detailing browser performance with the CSS print media type. In most recent browsers, Safari being the exception, the print stylesheet held up rendering of the page.
The zOomph article suggests a solution, to load print stylesheets using JavaScript once the page has rendered (ie, on the window.onload
event), with a backup for the JavaScript impaired. You can see their code in the original article.
Automating the task for WordPress
Most sites I develop are in WordPress so I decided to automate the process. This relies on using wp_enqueue_style
to register the stylesheets:
function enqueue_css(){
if (!is_admin()){
wp_enqueue_style (
'bigred-print', /* handle */
'/path-to/print.css', /* source */
null, /* no requirements */
'1.0', /* version */
'print' /* media type */
);
}
}
add_action('wp_print_styles', 'enqueue_css');
The above code will output the following HTML in the header:
<link rel='stylesheet' id='bigred-print-css' href='/path-to/print.css?ver=1.0' type='text/css' media='print' />
The first step is to wrap the above html in noscript tags, the WordPress filter style_loader_tag
is ideal for this.
function js_printcss($tag, $handle) {
global $wp_styles;
if ($wp_styles->registered[$handle]->args == 'print') {
$tag = "<noscript>" . $tag . "</noscript>";
}
return $tag;
}
add_filter('style_loader_tag', 'js_printcss', 5, 2);
The filter runs for all stylesheets, regardless of media type, so the function checks for print stylesheets and wraps them in the noscript
tag, other media types are left alone.
The first two arguments are the filter and function names respectively, the third argument specifies the timing (5 is the default) and the final argument tells WordPress how many arguments to pass to the filter – two – in this case $tag and $handle.
With the new filter, WordPress now outputs following HTML in the header:
<noscript>
<link rel='stylesheet' id='bigred-print-css' href='/path-to/print.css?ver=1' type='text/css' media='print' />
</noscript>
The next step is to add the JavaScript to load the stylesheets, we can do this by changing our original function, js_printcss
, and making use of a global variable:
$printCSS = '';
function js_printcss($tag, $handle){
global $wp_styles, $printCSS;
if ($wp_styles->registered[$handle]->args == 'print') {
$tag = "<noscript>" . $tag . "</noscript>";
preg_match("/<s*links+[^>]*hrefs*=s*["']?([^"' >]+)["' >]/", $tag, $hrefArray);
$href = $hrefArray[1];
$printCSS .= "var cssNode = document.createElement('link');";
$printCSS .= "cssNode.type = 'text/css';";
$printCSS .= "cssNode.rel = 'stylesheet';";
$printCSS .= "cssNode.href = '" . esc_js($href) . "';";
$printCSS .= "cssNode.media = 'print';";
$printCSS .= "document.getElementsByTagName("head")[0].appendChild(cssNode);";
}
return $tag;
}
The code creates the PHP variable $printCSS
globally, which is then called into the function using the global command.
After wrapping the tag in the noscript
tags, the new function uses a regular expression to extract the URL of the stylesheet from the link tag and placing it the variable $href
.
Having extracted the stylesheet’s URL, the function then appends the required JavaScript to the PHP global variable $printCSS
.
The final step is to add the JavaScript to the footer of the HTML using the wp_footer action in WordPress. The PHP to do this is:
function printCSS_scriptTags(){
global $printCSS;
if ($printCSS != '') {
echo "<script type='text/javascript'>n";
echo "window.onload = function(){n";
echo $printCSS;
echo "}n</script>";
}
}
add_action('wp_footer', 'printCSS_scriptTags');
The above code uses window.onload
as dictated in the original article. A better method would be to use an event listener to do the work, for those using jQuery we would change the function to:
function printCSS_scriptTags(){
global $printCSS;
if ($printCSS != '') {
echo "<script type='text/javascript'>n";
echo "jQuery(window).ready(function(){n";
echo $printCSS;
echo "});n</script>";
}
}
add_action('wp_footer', 'printCSS_scriptTags');
The above solution had been tested for very limited circumstances only and found to have worked. Were I to use the function in a production environment I would undertake further testing.
Another question to ask is whether all this is actually worth the effort – even when reduced through automation. On Big Red Tin, the print.css is 595 bytes, the delay in rendering is negligible.
Update Aug 23, 2010: Fixed a type in the code block redefining js_printcss
.
Update Aug 27, 2010: I’ve decided to release this as a plugin, get the skinny and the plugin from the followup article.