I keep everything in an external .js file. But not all functions are used on every page. Does this affect speed?

StackOverflow https://stackoverflow.com/questions/17041607

Question

My app's JavaScript/jQuery is contained in an external scripts.js file. It generally looks like this:

$('document').on('ready', function() {
    giraffe();
    elephant();
    zebra();
});

function giraffe() {
    // code
}

function elephant() {
    // code
}

function zebra() {
    // code
}

giraffe() is only used for the view available at /animal/giraffe
elephant() is only used for the view available at /animal/elephant
zebra() is only used for the view available at /animal/zebra,

But all 3 are supposed to run on the view available /animal/all. This is a rudimentary example, but this is the reasoning behind having them all in one .js file, apart from keeping HTTP requests to a minimum.

My question is, does this affect the JavaScript rendering? Even though giraffe() isn't used (has no elements to work on) on /animal/zebra, it's still being called. Does js/jQuery ignore the function if it finds nothing to do? I'm sure the whole script is read, and that probably takes time. So, what's best way to handle this?

One solution

To avoid conflicts, I've created conditionals at the top of the js file to only run the functions that the active page needs:

$('document').on('ready', function() {
    var body = $('body');

    if( body.hasClass('giraffe') ) {
        giraffe();
    }

    if( body.hasClass('elephant') ) {
        elephant();
    }

    if( body.hasClass('zebra') ) {
        zebra();
    }
});

This is a little more verbose than I would like, but it is successful in keeping these functions modular/conflict free. I welcome an improvement on this solution.

Was it helpful?

Solution

It would certainly be better to run just the code you need. It's not that difficult; the only minor complexity is handling your /animals/all case. If you want to keep all your code in one file, you could do it this way:

var animals = {
    giraffe: function() {
        console.log( "I'm a giraffe" );
    },
    elephant: function() {
        console.log( "I'm an elephant" );
    },
    zebra: function() {
        console.log( "I'm a zebra" );
    }
};

$(function() {
    var animal = location.pathname.split('/').pop();
    if( animal == 'all' ) {
        for( var animal in animals ) {
            animals[animal]();
        }
    }
    else if( animal in animals ) {
        animals[animal]();
    }
    else {
        console.log( "I'm a mystery animal" );
    }
});

You can actually test this code by going to URLs like this right here on Stack Overflow, and then pasting that code into the JavaScript console:

https://stackoverflow.com/animal/giraffe
https://stackoverflow.com/animal/elephant
https://stackoverflow.com/animal/zebra
https://stackoverflow.com/animal/ocelot
https://stackoverflow.com/animal/all

Update: OK, so you explained in the comment that the actual situation is a bit more complicated. I'll leave this code here in case it's useful for anyone, but for your situation you may be closer to what you need with the code you already have.

WRT the question of what one of your animal functions will do when you're on a page it doesn't relate to, of course that depends on how it's coded. It may successfully do nothing, or it may have an error, right?

Since we're talking about jQuery code, here are a couple of examples. Suppose you have code that's looking for an element by ID and then assumes that the element exists:

var zebraElement = $('#zebra')[0];
console.log( zebraElement.value );

On a page where the #zebra element exists, this code will log its value attribute. (I'm assuming for discussion that it's an element that has a value, such as an input element.)

But if #zebra is not present, then zebraElement is undefined, and attemping to access its value will fail and land in the debugger.

OTOH, if you coded that like this:

var $zebra = $('#zebra');
console.log( $zebra.val() );

it wouldn't fail if #zebra is missing, it would successfully print undefined without causing an error.

Similarly, if you have code that uses $().each(), it will typically run without failure when the element(s) are missing, because it simply won't execute the callback function:

$('.animal').each( function( i, e ) {
    console.log( $(e).val() );
});

If there are no elements with class="animal", then the console.log() call will never be reached. So there's no error, it just doesn't do anything.

Depending on what you're doing, this can be a perfectly reasonable way to fire up only the behavior you need—simply make sure your code handles the missing DOM elements by cleanly doing nothing.

Also be sure to read nick's answer for more insights.

And one more update… In the comment you mentioned keying off classnames on the body element. A nice way to do that would be similar to the code example above. You don't need a conditional for each animal, just a loop and a single conditional:

var animals = {
    giraffe: function() {
        console.log( "I'm a giraffe" );
    },
    elephant: function() {
        console.log( "I'm an elephant" );
    },
    zebra: function() {
        console.log( "I'm a zebra" );
    }
};

$(function() {
    var $body = $('body');
    for( var animal in animals ) {
        if( $body.hasClass(animal) ) {
            animals[animal]();
        }
    }
});

So, for example, if you have <body class="giraffe zebra"> it will call the animals.giraffe() and animals.zebra() functions.

OTHER TIPS

There are actually two issues here, one is whether to load all the functions and the other is whether to run them all. The question about whether to run them all (and some solutions to running just the functions you need) has been well addressed by Michael Geary in the other answers.

So, should you load them all? You need to weigh up the time needed to download a larger file over making extra http requests. With only three 'groups' of functions shared between 3 pages (and one page using all of them) and when you expect the user to be changing pages regularly I would go with one file.

Once your project gets very big you'll want to use something like requirejs to manage your scripts and load them asynchronously. Loading your js scripts asynchronously will increase the speed of the page (and the perceived speed of the page) and if there are issues (especially with scripts hosted by third-parties) your page wont get help up. You can of course do this without a library:

With HTML5:

<script async src="http://google.com/script.js"></script>

There are a million ways to load asynchronously with pure JS, one way:

<script>
  var resource = document.createElement('script'); 
  resource.src = "//google.com/script.js";
  var script = document.getElementsByTagName('script')[0];
  script.parentNode.insertBefore(resource, script);
</script>

Javascript will ignore any function that is not "called". Ie. if you do not call elephant() on any page other than the /elephant or /all page then it will never be used, nor give you any errors.

However, you may wish to not display functions on pages it is not being used for faster page loading. Consider, separate JS files for certain pages, or by loading certain JS scripts from a back-end server such as PHP to determine which functions are needed.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top