debuggable

 
Contact Us
 

Include only the JS you really need

Posted on 18/11/06 by Felix Geisendörfer

Deprecated post

The authors of this post have marked it as deprecated. This means the information displayed is most likely outdated, inaccurate, boring or a combination of all three.

Policy: We never delete deprecated posts, but they are not listed in our categories or show up in the search anymore.

Comments: You can continue to leave comments on this post, but please consult Google or our search first if you want to get an answer ; ).

Ok, this won't be a ground breaking post. If you are using some JS in your application you know that those libraries can add up rather quickely. However, most people hate to load hundreds of KB in JS toys when coming to your page. It's too much. Therefor it's a smart idea to only include the JS that's really needed. In CakePHP this often directly related to the Controller/action that is running.

I'm fairly sure most people have run into this problem before. I'm also sure those people have figured out a way to solve the problem. Nevertheless, here comes my current approach on dynamically selecting which JS scripts to include on a certain page.

My solution simply adds 2 function and a member variable to the AppController ands looks like this:

class AppController extends Controller
{    
    var $jsIncludes = array('jquery/jquery');
       
    function beforeRender()
    {
        $this->set('jsIncludes', $this->jsIncludes);
    }
   
    function includeJs($jsPath)
    {
        if (!in_array($jsPath, $this->jsIncludes))
            $this->jsIncludes[] = $jsPath;
    }
   
    function excludeJs($jsPath)
    {
        $arrayPos = array_search($jsPath, $this->jsIncludes);
       
        if ($arrayPos!==false)
            array_splice($this->jsIncludes, $arrayPos, 1);
    }
}

So by setting a value for var $jsIncludes you can define your default includes that you always want to have included. Then you can use the includeJs/excludeJs function inside controller actions (or filters) to fine grain what libs you want to include. For example in my MenuEntriesController I have a beforeRender that looks like this:

class MenuEntriesController extends AppController
{
    var $name = "MenuEntries";
    var $uses = array('MenuEntry', 'Menu');
   
    function beforeRender()
    {
        $this->includeJs('jquery/interface');
        parent::beforeRender();        
    }
}

And now the missing piece, the integration in your default.thtml layout:

<?php echo $javascript->link($jsInclude.((DEBUG > 0) ? '.js?hash='.md5(time()) : '')); ?>

By putting this little loop into your section, all JS libs will automatically get included. And even better, when DEBUG is set to a value bigger then 0, adding a unique hash to the include path will make sure your JS will never be cached.

So if up to now you've always included all JS for every page load, this might help to reduce bandwidth usage for both you, and the users of your application. I know this is not the most advanced solution one could come up with. I thought about using my light weight ACL algorithm for the problem. But then I decided that it's too simple a problem for that kind of bloat.

Btw. there is also a HeadHelper created by RosSoft that will allow you to include JS/CSS on a per view basis. However, most of my JS is included on a per Controller basis, so I created my own approach.

-- Felix Geisendörfer aka the_undefined

 

You can skip to the end and add a comment.

Robert  said on Nov 19, 2006:

Beats the hell out of my way (examining the $this->params['url']['url']). Think I will change to your idea and never talk of my way again ;)

Cherrio.

Mandy said on Nov 19, 2006:

Nice Way!

However, what I don't understand is that why would you go down this path?

In most of the cases, the people working on the view would be different from people working on the controllers. So, why tie these up?

The control for javascript should remain with the view developers (or web developers or front end developers) and the backend guys (working on controllers) should not mix this in their code.

It's fine for those who are working on the entire application (view & controllers).

What do you think?

Felix Geisendörfer said on Nov 19, 2006:

Mandy: I agree with what you said. It just depends on the level of abstraction one has to achieve. The project I am using this piece of code in is a one man gig. I am the one doing the Views, Controllers, etc.. I would only make my life harder by adding another layer of abstraction. So this solution is ment as a pragmatic approach for smaller projects. However, for a bigger application I could imagine some other solutions to work better. Maybe a smart element that knows it all (what JS to include where), or maybe a solution like RosSoft's HeadHelper. If you got something in mind and feel like writing in your blog about it, let me know and I'll add a link to your solution in this post as well ; ).

Tarique Sani said on Nov 20, 2006:

Mandy: may be you are looking for something like http://bie.no/blog/computers/software-engineering/javascript/2006/10/dynamic-loading-of-javascript/

MP:Schorsch said on Nov 20, 2006:

I´m making extensive use of the headhelper for exactly the same reason and js lib (jquery just rocks).

my js differers on a per view(function) basis but still i might use your approach in another project.

PHPDeveloper.org said on Nov 20, 2006:

Felix Geisendorfer's Blog: Include only the JS you really need...

...

[...] Now combine this technique with my Include only the JS you really need post and you've got yourself a very simple, yet elegant way to bring some structure to your javascript code. [...]

dz  said on Apr 05, 2007:

Hello,

another problems occurs when you have a lot of smaller js libraries to load (lets say: jquery, plugin1, plugin2, plugin3, your application js code, action code etc..)

this might slow down the load time significantly... so we used to join them all and then let the user download just one file. I wonder if there is an easy way to do sth like this in cake?
maybe a generic js controller?

regards
danielz

This post is too old. We do not allow comments here anymore in order to fight spam. If you have real feedback or questions for the post, please contact us.