Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

JavaScript doesn't offer a module system. There are many third-party solutions, like require.js or jQuery's $.getScript. Most, while good, they bring dependencies, extra kbs and/or limit your folder structures. Sometimes it's necessary to make a .js file self-sufficient. This said, what is the smallest snippet that can be inserted at the top of a .js file, which will emulate "import", so it can load other scripts before running itself?

The .js file should look like this, for example. Notice the addition is just a small one-liner:

(function(i,m,p,o,r,t){for(p=i.length,o=0;o<p;++o){r=(t=document).createElement('script');r.src=i[o];r.onload=function(){!--p&&m()};t.head.appendChild(r); }; })
(['http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js','http://underscorejs.org/underscore-min.js'],
function main(){
    console.log('jQuery loaded:',$);
    console.log('underscore.js loaded:',_);
});

For clarity, the top function above, expanded, is equivalent to:

(function (u, f, l, i, a, d) {
        for (l = u.length, i = 0; i < l; ++i) {
            a = (d = document).createElement('script');
            a.src = u[i];
            a.onload = function () {
                !--l && f()
            };
            d.head.appendChild(a);
        };
    })

Can this be minimized even further?

share|improve this question
1  
Awful and should't be done, but phpied.com/javascript-include –  jacktheripper Nov 24 '12 at 22:04
6  
@Dokkat If you wish for people to make suggestions about how this could be improved, it's best not to minify it. –  Jonathan Sampson Nov 24 '12 at 22:11
4  
@Dokkat Fine, if you want people to understand the concept, don't make it more difficult by providing minified code. –  Jonathan Sampson Nov 24 '12 at 22:13
3  
@Dokkat Jesus, stop minifying your code. I'm trying to read it! :P –  Šime Vidas Nov 24 '12 at 22:19
1  
dean.edwards.name/packer –  David Nov 24 '12 at 22:31
show 13 more comments

migrated from stackoverflow.com Nov 24 '12 at 22:26

This question came from our site for professional and enthusiast programmers.

4 Answers

up vote 1 down vote accepted

Most JavaScript libraries add properties to the window object so one alternative would be to test for those properties. For example

m = [ "$", //jQuery
      "_"  // underscore.js
    ];
tries = 5000;
(function loaded( ) {
    var i = m.length;

    while( i-- ) {
        if( !window[m[i]] ) { 
            return --tries ? setTimeout( loaded, 1 ) : failed();
        }
    }
    done();  
}())

And in one line it looks like this

m=["$","_"];tries=5e3;(function e(){var t=m.length;while(t--){if(!window[m[t]]){return--tries?setTimeout(e,1):failed()}}done()})()
share|improve this answer
    
I don't think this is really relevant. The question is not how to detect the all of the external dependencies have been loaded. –  svick Nov 25 '12 at 13:12
    
To be honest, while this doesn't answer, it's better than the other answers as it (almost) does what I asked. I'll stay with my own snippet this time. ); –  Dokkat Nov 26 '12 at 2:06
add comment

Here is what I do:

var a_script= document.createElement('script');
a_script.src = 'http://www.blah.com/some_script.js';
document.head.appendChild(a_script);

I should mention that in production I pretty much always combine all javascript files into one larger, minified, and google closure compiled file.

share|improve this answer
1  
He's actually doing the same thing, but his fires off on the load event so it's a little more precise. –  Keith Nov 24 '12 at 22:19
    
I do almost the same thing, I usually append to the body though. –  travis Nov 25 '12 at 5:20
add comment

I normally wouldn't suggest to compress this specific JavaScript file. The reason is because it's really powerful technique what you are trying to achieve, and it's on the included files where you need to put the effort on the compression, as they will tend to be longer than such a script as you are describing.

I personally use the following snippet, which I consider short because I have seen that it's unobtrusive, not sure if it's shorter than the one you are suggesting.

//JP_js-css_request.js

<!-- Append js and css to the <head> of the document on demand based on the page -->

function loadjscssfile(filename, filetype){

    switch (filetype)
    {
        case "js":
        {
            var fileref=document.createElement('script')

            fileref.setAttribute("type","text/javascript");
            fileref.setAttribute("src", filename);
        }
        break;

        case "css":
        {
            var fileref=document.createElement("link")
            fileref.setAttribute("rel", "stylesheet");
            fileref.setAttribute("type", "text/css");
            fileref.setAttribute("href", filename);
        }
        break;

        default: break;
    }


    if (typeof fileref!="undefined")  document.getElementsByTagName("head")[0].appendChild(fileref)
}

var filesadded="" //list of files already added

function checkloadjscssfile(filename, filetype){
    if (filesadded.indexOf("["+filename+"]")==-1){
        loadjscssfile(filename, filetype)
        filesadded+="["+filename+"]" //add to list of files already added, in the form of "[filename1],[filename2],etc"
    }
}

I place this script in the head as and when I want to call a CSS or JS to be re-used but I want to check if it's been included already at the head we call loadjscssfile( [path_to_file/filename.extension], js or css.

That for me takes care of it. Also keeping this file editable (as it kind of short) allows you to do modifications that can affect many files and centralizing the process. Or for example if you want to add import for CSS, etc. One thing to mention is that if you are including jQuery libraries or Prototype, or Scriptaculous etc, you need to be careful to call this file at the end </head>. The reason is that if it appends the file after a library can affect the bindings of functions related by another library.

So in short if you have

jQuery
jQuery UI v-xxx
Mootools
Scriptaculous
Prototype

libraries, you need to not only avoid conflict between libraries but to ensure that all JS framework or heavy stuff are loaded firstly. That ensures that you can call files to append to the head just after the JavaScript file is included. So far, it has heavily helped me out because I use http://jscompress.com and after checking a JS works good on a project, I compress the file and call loadjscssfile() as a function over that one. Although sometimes it takes some time to load depending on how big the resource is.

share|improve this answer
    
Also, you should look into this framework, might be just what you are looking for. EnhanceJS version 1.1 - Test-Driven Progressive Enhancement * enhancejs.googlecode.com –  Jean Paul Nov 25 '12 at 1:44
add comment

I advise against your "smallest" approach, I think you set your priorities wrong.

IMO you should avoid:

  • Minifying non-production code, as processing time is cheaper than developer time;
  • Premature optimization, such as declaring extra arguments to save 4 characters by avoiding using var (for 3 variables, that is 1.3 characters saved per variable);
  • Using variables with 3 or less characters, 1-char variables being the Megazord of this flaw.

All these things will reduce the readability and maintainability of your code. And what for? The gain is infinitesimal, and the risk is large.

Just including the minified JQuery you give as an example increases the code by 93 thousand characters. What is the use of worrying about saving 50 characters out of 100k?

You can implement your JS normally and then minify in production, you can reduce image sizes, you can use CSS sprites... there are a lot of better ways to save bandwidth.
(YMMV)

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.