SSbits - Home page
Site by Carbon Crayon
Submit a Post >

Snippets - Little bits of code to make you happy

Dynamically Combining CSS and Javascript Files for Faster Loading using Requirements

As you will most likely already know, reducing the number of HTTP requests that your site makes can have a significant effect on loading times. However, usually it's not practical to combine all your files into a single one for reasons of maintainability. This is where SilverStripe steps in and provides an elegant, automated solution to this common 'requirement' (lol, get it?).

So effectively all we need to do is tell SilverStripe which files we want it to combine and where we want it to place the combined file. In this example we are doing so with CSS, however this same code will work just as well with Javascript files (it will even minify then using JSMIN!).

Note. SilverStripe will only use the combined file when your site is in Live mode. This let's you tweak your individual CSS files without having to recombine them each time the page loads while developing the site.

So here is the code inside the init() function of our Page_Controller class in Page.php.

class Page_Controller extends ContentController {

	public function init() 
	{
		parent::init();

		//Set our theme's root folder
		$themeFolder = $this->ThemeDir();
	
		//Add all the files to combine into an array
		$CSSFiles = array(
			$themeFolder . '/css/layout.css',
			$themeFolder . '/css/typography.css',
			$themeFolder . '/css/form.css'
		);
		
		//Set the folder to inside our theme so that relative css image paths work
		Requirements::set_combined_files_folder($themeFolder . 'combinedfiles');
		
		//Combine!	
		Requirements::combine_files("combinedCSS.css", $CSSFiles);
	}
}

So first thing we do in order to keep this dynamic is we generate the current themes directory and assign it to $themeFolder. We then create an array with the paths to all of the CSS files we want to combine. Then before combining them we set the folder for the resulting combined file to be in the themes root directory in a folder named combinedfiles (which SilverStripe will create if it doesn't exist) which means the combined file is at the same level as the original CSS files. This is important because by default SilverStripe will place combined files into the assets folder, where any relative image paths in the CSS will no longer work.

Finally we call the combine_files() function, passing it the name of the file we want to create and the array of file paths, before reloading our page to see a single CSS include in the head! Awsome :)

Note. If your files are not included at all, make sure that your theme folder has the correct permissions to enable SilverStripe to create the combinedfiles folder.

And remember! This will only have an effect in LIVE mode!

Live example

Here is a complete example from the SSbits's very own init() function (you can see the resulting files included in this page!):

        //Set our theme's CSS folder
        $themeFolder = $this->ThemeDir();
  
        //Set the folder to our theme so that relative image paths work
        Requirements::set_combined_files_folder($themeFolder . '/combinedfiles');

		/*
		 * 
		 * CSS Includes
		 * 
		 */
     
        //Add all the files to combine into an array
        $CSSFiles = array(
            $themeFolder . '/css/layout.css',
            $themeFolder . '/css/typography.css',
            $themeFolder . '/css/form.css',
			'syntaxhl/styles/shCore.css',
			'syntaxhl/styles/shThemeDefault.css'
        );

        //Combine!  
        Requirements::combine_files("combinedCSS.css", $CSSFiles);

		/*
		 * 
		 * JS Includes
		 * 
		 */

		Validator::set_javascript_validation_handler('none'); 
		
		//CDN - dont combine or we lose the possibility of cached versions in the users browser cache
		Requirements::javascript("http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js");
		Requirements::javascript("http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js");	

        //Add all our files to combine into an array
        $JSFiles = array(
			"mysite/javascript/jquery.utils.js",
			"syntaxhl/scripts/shCore.js",
			"syntaxhl/scripts/shBrushPhp.js",
			"syntaxhl/scripts/shBrushXml.js",
			"syntaxhl/scripts/shBrushCss.js",
			"syntaxhl/scripts/shBrushSql.js",
			"syntaxhl/scripts/shBrushJScript.js"
        );	
         
        //Combine!  
        Requirements::combine_files("combinedJS.js", $JSFiles);

Configuration (optional)

If you would rather SilverStripe didn't minify your JS then you can disable this using this in your _config.php:

Requirements::combine_js_with_jsmin = false; 

If you would rather SilverStripe didn't add your JS include into the <body> and put it in the <head> instead you can add this to your _config.php:

Requirements::set_write_js_to_body(false); 

 

Special Thanks

Special thanks go to Bart van Irsel, Frank Mullenger for their contributions to this post.

Aram Balakjian avatar

Aram Balakjian

Aram is a web developer running London based agency Aab Web. He has a strong passion for developing attractive, usable sites around the SilverStripe CMS.

  • Liam Houlahan
    04/12/2010 11:28pm (3 years ago)

    Hi Aram this is awsome... I was going to explore how to do this exact requirement. I assume this will work with javascript files aswell?

    Thanks
    Liam

  • Aram Balakjian
    05/12/2010 12:54am (3 years ago)

    Hi Liam, yep work just the same with JS :)

  • alex li
    05/12/2010 9:07pm (3 years ago)

    The combination of a set of javascripts could be useful as well.
    Just the output will put inside the body tag, may casue some problem since the order of the javascripts is different from putting into <head> part.

    Good tutorial anyway.. :D
    Thanks 4 sharing


  • MRKDevelopment
    06/12/2010 4:15am (3 years ago)

    It would be great to do a minify at the same time.

    Is there a way to get this to work in the Head tag ?

  • Bart van Irsel
    06/12/2010 8:20am (3 years ago)

    Great tutorial Aram!

    @alexli Maybe forcing requirements to go into head might help?
    (but that might decrease performance on pageload as well)

    Requirements::set_write_js_to_body(false);

    What do you mean with the order of javascript? i think it's the same order as Aran did in $CSSFiles array?

    @MRKDev The Silverstripe book says Requirements::combine_files() uses the JSMIN library by default which can be deactivated by using this statement:

    Requirements::combine_js_with_jsmin = false;

  • Aram Balakjian
    06/12/2010 8:11pm (3 years ago)

    Thanks Bart, I wasn't aware it minified JS too!

    I have now included an example with both CSS and JS :)

    Aram

  • Frank Mullenger
    06/12/2010 9:38pm (3 years ago)

    Instead of:
    $themeFolder = 'themes/' . SSViewer::current_theme();

    I think you can use:
    $themeFolder = $this->ThemeDir();

  • Aram Balakjian
    07/12/2010 10:33am (3 years ago)

    Thanks Frank, had been looking for that! post amended :)

  • Francisco Arenas
    01/03/2011 2:24pm (3 years ago)

    you need to use: Requirements::set_combined_files_enabled(false); now.
    the other sintax is not working anymore

  • David Montgomery
    23/03/2011 9:20am (3 years ago)

    This is great and I have been using it on a few website already. Thanks.

    This code minifies the JS but what about the CSS? Is there a way to minify this as well?

  • Simon Erkelens
    28/03/2011 6:36pm (3 years ago)

    Top (experience) tip!

    Start any of your Javascript files with a ; !
    This way, when combining, all goes well. Any extra's are okay in javascript, forgetting one will give you a headache!

    In case you use FirePHP, here's one (quite nice, if I may say so) code to start off with. With this code, you can use $.fb(output.here) to output stuff to the console:

    ;if (undefined === window.DEV) window.DEV = false;if (undefined === jQuery.fb) {jQuery.fb = function() {if (DEV && window.console) console.info(arguments);};}

    Put this at the top of your mysite/javascript/site.js, and all will be ok. Yes, this code doesn't end with ;, but then again, it doesn't have to in my case. Adding it shouldn't be a problem if I'm correct.

    This tiny piece of code will put every $.fb output to your console IF the site is in dev-mode.

  • Darren-Lee
    09/04/2011 1:53pm (3 years ago)

    Thanks for the example, dude. I'm going to use this right now on a project that I am just in the process of completing!

  • Pali
    24/04/2011 2:42pm (3 years ago)

    if you want to use css combining if you use themedCSS calls like this:

    Requirements::themedCSS('content');
    Requirements::themedCSS('typography');
    Requirements::themedCSS('print');

    use this for css combining:


    Requirements::combine_files('combined.css', array_keys(Requirements::backend()->get_css()));

  • Pali
    24/04/2011 2:44pm (3 years ago)

    and don't forget to use absolute paths for images in your css files, because final css location will be changed!

  • Aram Balakjian
    26/04/2011 9:22am (3 years ago)

    @Pali - that is what $themeFolder = $this->ThemeDir(); is for, this will ensure that the combines CSS is in the same relative place as the origonal CSS files so you can use relative paths for images.

    Aram

  • sashion
    23/05/2011 11:37pm (3 years ago)

    Great tut! thanx!
    Is there a way to combine print-styles with the other styles as well or do i have to define them separatey like
    Requirements::themedCSS($themeFolder . '/css/print.css', 'print');

    cheers

  • sashion
    24/05/2011 5:21pm (3 years ago)

    found it out: either you require the print styles separately or - even better - you just define them in the main css-file via @media print { }

  • smares
    26/05/2011 11:10pm (3 years ago)

    You should change "combinedfiles" to "/combinedfiles" in your example. ;)

    Is there a way to combine CSS / JS files defined in the templates?

  • Mom Bunheng
    03/07/2011 9:51am (3 years ago)

    How to call combined css file within templates?

  • Mom Bunheng
    03/07/2011 10:29am (3 years ago)

    Hi
    I am trying to use it as well but still see css include in my template as separated files.

    Thanks

  • Mom Bunheng
    04/07/2011 9:22am (3 years ago)

    Bunheng

    Ohh, now it is working, because It work only live mode not dev mode.

    Thanks

Post a comment ...

You cannot post comments until you have logged in. Login Here.

Advertisement

Site of the Month

Find SSbits on

Top Contributers

Rank Avatar Name
1 article image Aram Balakjian
2 article image Daniel Hensby
3 article image Marcus Dalgren
4 article image Hamish Campbell
5 article image Ty Barho
6 article image njorndare
7 article image Martijn van Nieuwenhoven
8 article image Darren-Lee
9 article image Roman Schmid
10 article image Matt Clegg

View full leaderboard


Advertisement