So, how do we fix this? Well, our first inclination might be to move
<?php print $scripts; ?> down, so it sits just before the closing
<head>, in order for it to work.
The other potential side effect happens when we turn on Drupal's built-in JS aggregation (which we always do, right?) By just moving the
html.tpl.php template. The header scope is what is printed with the
$scripts variable. When you add
<?php print $scripts; ?> to your template, Drupal will print out all the scripts scoped to header, nicely formatted in
Somewhat less intuitive, the footer scope doesn't have its own specific variable. Its scripts are slapped on to the end of the
$page_bottom region variable. So, if we just move
$scripts down to the bottom of our template, we are potentially printing two groups of scripts that could be aggregated into one. There is a better way.
hook_js_alter allows us to modify any scripts added via
drupal_add_js(). This hook fires during
drupal_get_js(), before scripts are aggregated and printed to the screen. If the scope is not specified in
drupal_add_js(), Drupal will set the scope to header by default. Using
hook_js_alter(), we can go through the list of scripts and change the scope of each to footer. The code goes in your theme's
template.php file and is actually quite simple:
So, what is this doing? First we create an array of scripts we want to stay in the header. In this example, we are using modernizr (in conjunction with the modernizr module) which includes html5shiv.js. As mentioned earlier, html5shiv.js needs to be loaded in the
<head> tag to function properly. Note: Creating this array isn't really necessary, being that it only contains one script. However, doing so will make it easier, should we need to move more scripts to the top of the page down the road. Next we cycle through each script checking to see if its scope is currently set to header and it is NOT in our array. If those conditions are met, we change the scope to footer. That's it. No need to muck with any template files.
Now let's see what yslow has to say.
Yeah, that's what I thought, yslow. It's worth noting that some scripts may have been set to header on purpose by their module developer. There really isn't any way, that I know of, to tell whether or not a script was scoped to header on purpose or if it was set by default. As this is a blanket approach, you'll just need to be mindful of your scripts and their loading order.
As mentioned earlier, scope is one of many criteria used to determine aggregation and script load order. To learn more, check out the drupal_add_js() api.