Wordpress - Can I add styles to footer with $wp_styles->add_data?
As @pieter-goosen and @birgire in other answer and comments, W3C specs do not allow css in footer, quote from specs page linked:
A
link
element must have arel
attribute.If the
rel
attribute is used, the element is restricted to thehead
element.
Aside from that, you should note that when you add css in footer, when someone opens your page it appears unstyled until the full css is loaded: with slow connections your page will appear broken for some seconds, elements hidden via css will be visible, sliders appear like ul lists of visible images...
So, really, I think it's a bad idea.
That said, I don't like the "don't do that" kind of answers, I prefer the "you can do that in this way, but I suggest don't do that" kind.
So, here the way to do that, but hey, I suggest to don't do that.
Things are more easy as you can expect: scripts and styles should be added on wp_enqueue_scripts
hook, but if you enqueue styles after wp_head
hook has been fired, then scripts and styles are added in the footer.
An useful application of that is when scripts are enqueued inside a shortcode callback, adding possibility to add shortcode-specific scripts with ease.
Regarding styles, WordPress is not intransigent about W3C statements and will put styles on footer unblinkingly.
Workflow:
- hook
'wp_print_styles'
, when all scripts and styles are already enqueued, but not printed - store current queue for scripts and styles in variables
- empty the queue for both scripts and styles, in this way, when WordPress look to print scripts and styles in head will find nothing to print
- hook
'wp_footer'
with priority 0, and restore the styles and scripts queue you stored at point #2, in this way, when WordPress look for styles and scripts to print in footer can find and print them.
All can be done in few lines of code:
add_action('wp_print_styles', function() {
if ( ! doing_action( 'wp_head' ) ) { // ensure we are on head
return;
}
global $wp_scripts, $wp_styles;
// save actual queued scripts and styles
$queued_scripts = $wp_scripts->queue;
$queued_styles = $wp_styles->queue;
// empty the scripts and styles queue
$wp_scripts->queue = array();
$wp_styles->queue = array();
$wp_scripts->to_do = array();
$wp_styles->to_do = array();
add_action( 'wp_footer', function() use( $queued_scripts, $queued_styles ) {
// reset the queue to print scripts and styles in footer
global $wp_scripts, $wp_styles;
$wp_scripts->queue = $queued_scripts;
$wp_styles->queue = $queued_styles;
$wp_scripts->to_do = $queued_scripts;
$wp_styles->to_do = $queued_styles;
}, 0 );
}, 0);
Note
Code above require PHP 5.3+ for the usage of closure and WP 3.9 for the usage of doing_action
.
I don't know what your exact reason is for this, but you should scrap the idea of moving styles to the footer. If you though of a gain in speed, you might gain a unnoticable amount, if any, but that will be at the cost of other bigger things.
Styles should always be added inside the <head></head>
tag. The reason is that <style>
tags outside the head tags are invalid HTML. That is why you don't have that option to load styles in the footer with wp_enqueue_style
and wp_register_style
like you have with scripts.
I would seriously rethink this whole idea.
EDIT
As a sidenote from a comment to this answer from @G.M.
wp_enqueue_style
add<link>
tag, not<style>
one. However,<link>
tag is not allowed outside<head>
too
I was recently researching this very question specifically because of the requirement from Google PageSpeed Insights:
Eliminate render-blocking JavaScript and CSS in above-the-fold content
Your page has 1 blocking script resources and 1 blocking CSS resources. This causes a delay in rendering your page.
None of the above-the-fold content on your page could be rendered without waiting for the following resources to load. Try to defer or asynchronously load blocking resources, or inline the critical portions of those resources directly in the HTML.
Optimize CSS Delivery of the following:
.../style.min.css?ver=4.8
Google's recommendation was to embed critical CSS using an inline <style>...</style>
block in the <head>...</head>
and load non-critical CSS via JavaScript.
I wasn't able to figure out how to use WordPress's native functions to enqueue a native style block without first requiring an external stylesheet (see: wp_add_inline_style
. So, I just completed the task using my own custom header.php
and footer.php
template files:
header.php
<!-- ... -->
<?php wp_head(); ?>
<style id="critical-styles" type="text/css" media="screen">html { background: black; } body { opacity: 0; }</style>
</head>
<!-- ... -->
footer.php
<!-- ... -->
<noscript id="deferred-styles">
<link rel="stylesheet" type="text/css" href="<?php echo get_stylesheet_directory_uri() . '/style.min.css?' . urb_get_version(); ?>" />
</noscript>
<script>
var loadDeferredStyles = function() {
var addStylesNode = document.getElementById('deferred-styles');
var replacement = document.createElement('div');
replacement.innerHTML = addStylesNode.textContent;
document.body.appendChild(replacement)
addStylesNode.parentElement.removeChild(addStylesNode);
};
var raf = requestAnimationFrame || mozRequestAnimationFrame || webkitRequestAnimationFrame || msRequestAnimationFrame;
if (raf) raf(function() { window.setTimeout(loadDeferredStyles, 0); });
else window.addEventListener('load', loadDeferredStyles);
</script>
<?php wp_footer(); ?>
</body>
</html>
Note: My critical style block includes: body { opacity: 0; }
as a way to hide un-styled content. Once my deferred stylesheet is loaded, it includes body { opacity: 1; }
to clear this attribute with a transition.