In our previous post, we talked about a mod to Concrete5 which would allow for flexible JS and CSS includes. In this post, we’re going to expand upon that idea with another mod which will allow us to move our Javascript code to the bottom of the HTML source, thereby providing better front-end performance for our websites.
Note: This mod only takes effect when used in conjunction with the Flexible JS and CSS Includes mod, and will have no effect on core Concrete5 Javascript includes. Thus its true benefit—at least right now—is for the public-facing end of your website, not the contextual-editing part of Concrete5.
Eventually, it would be nice to see this functionality incorporated into the Concrete5 core. Also it would be nice to see all core Javascript includes migrated to use these methods. That way, all JS scripts would be loaded last on the page but in the correct order, preserving dependencies, etc.
Instructions for Concrete v5.4
Step 1:
Copy the /concrete/libraries/view.php file to your /libraries folder.
Step 2:
Within view.php, add a private property, $footerItems, immediately after $headerItems.
private $footerItems = array();
Step 3:
Still within view.php, replace the stock outputHeaderItems() method with the following:
public function outputHeaderItems() {
$items = $this->getHeaderItems();
foreach($items as $item) {
if ($item instanceof SiteJavaScriptOutputObject) {
$this->footerItems[] = $item;
} else {
print $item;
print "\n";
}
}
}
This is where the magic happens. We are going through the headerItems array and checking to see if there are any Javascript items that have used our modified HtmlHelper class. If so, we pull them aside and stick them in a footerItems array for later inclusion.
Step 4:
Still within view.php, add the following method:
public function outputFooterItems() {
$items = $this->footerItems;
foreach($items as $item) {
print $item;
print "\n";
}
}
Step 5:
Create a new file in your /elements directory called footer_required.php. Insert the following code:
<?php
print $this->outputFooterItems();
echo Config::get('SITE_TRACKING_CODE');
?>
Step 6:
Now, inside your template, insert the following code, just before the closing </body> tag:
<?php Loader::element('footer_required'); ?>
Step 7:
Finally, whenever you include Javascript code, use the following syntax:
$this->addHeaderItems($html->javascript(array(
'url' => 'myScript.js'
// and any of the other optional parameters here
)));
That’s it! Your Javascript code will now appear last in the HTML source, and you will likely notice an improvement in front-end performance.
Hints
Modify header_required.php to use the new HtmlHelper methods
But only do this when the user is not logged in (as it will break the Concrete5 UI when the user is logged in).
if ($u->isRegistered()) {
$this->addHeaderItem($html->css('ccm.base.css'), 'CORE');
$this->addHeaderItem($html->javascript('jquery.js'), 'CORE');
$this->addHeaderItem($html->javascript('ccm.base.js'), 'CORE');
} else {
$this->addHeaderItem($html->css('ccm.base.css'), 'CORE');
$this->addHeaderItem($html->javascript(array('url' => 'jquery.js'), 'CORE'));
$this->addHeaderItem($html->javascript(array('url' => 'ccm.base.js'), 'CORE'));
}
And as mentioned elsewhere, don’t even bother to include the jQuery library in this file unless you’re using jQuery elsewhere on your site.
Put your JS and CSS code inline if on the home page, otherwise link externally
Usually, front pages profit from having the JS and CSS code put inline, thereby cutting down on initial HTTP requests. Internal pages to a website, on the other hand, usually benefit from the caching provided by linking to external files.
$this->addHeaderItem($html->css(array(
'url' => 'styles/globalLayout.css',
'inline' => $this->getCollectionParentID() ? false : true
)));
If we are on the home page (the collection’s parentID == 0, which is falsey) then our code is inserted inline. If not, we use an external link.
Use addHeaderItem() to add footer items?
It may seem a little strange to be using addHeaderItems in order to add Javascript to the footer. But we’re doing this for the sake of backward-compatibility. Adding a new method of addFooterItems would not work with existing code.