One JS File for Multiple Pages

JavascriptFilesystems

Javascript Problem Overview


I typically put all the JavaScript scripts into one file e.g. scripts.js (the less HTTP request, the better). So, as expected, some scripts are needed for some pages, some aren't.

To target a specific page, I use something like:

if ($("body#share").length > 0) {
    // Place the logic pertaining to the page with ID 'share' here...
}

// The file / script continues...

Other or better suggestions? Thanks!

Clarification: I was not looking for the pros / cons between consolidating multiple JS files into one big file and keeping multiple separate JS files. The answer for this is surely 'depends on the situation' (we know that). My question is, assuming all my JS logic is placed into one big file, how do I make a particular (chunk of) script runs only when the corresponding page is loaded? One way I used to do is using if ($('#id-of-target-element')) { /* run the script */}; is there a better way?

Javascript Solutions


Solution 1 - Javascript

I like Paul Irish's approach... you don't have to follow it exactly, but the general idea is a very solid one.

It might look something like this for your example

Html

<body id="share">

Your page specific javascript

YourNamespace = {
  share : {
    init : function(){
      // Place the logic pertaining to the page with ID 'share' here...
    }
  }
}

Paul Irish's Javascript that makes the magic happen

UTIL = { 
  fire : function(func,funcname, args){
    var namespace = YourNamespace;  // indicate your obj literal namespace here

    funcname = (funcname === undefined) ? 'init' : funcname;
    if (func !== '' && namespace[func] && typeof namespace[func][funcname] == 'function'){
      namespace[func][funcname](args);
    }
  }, 

  loadEvents : function(){
    var bodyId = document.body.id;

    // hit up common first.
    UTIL.fire('common');

    // do all the classes too.
    $.each(document.body.className.split(/\s+/),function(i,classnm){
      UTIL.fire(classnm);
      UTIL.fire(classnm,bodyId);
    });

    UTIL.fire('common','finalize');
  }
};

// kick it all off here 
$(document).ready(UTIL.loadEvents);

So the line you see directly above will kick off the following

YourNamespace.common.init()
YourNamespace.share.init()
YourNamespace.common.finalize()

Have a read of his blog post and a few of the variations linked from it.

Solution 2 - Javascript

Similar questions have been already asked and the correct answer was and always will be

> It depends on the situation.

However, if your concern is about minimizing the round-trip time (RTT) then it is certain that

> Combining external scripts into as few files as possible cuts down on > RTTs and delays in downloading other resources.

It is good to keep it as few as possible, but you don't necessarily have to keep it into one file strictly.

Let's take a look at why it is so.

> While partitioning code into modular software components is a good > engineering practice, importing modules into an HTML page one at a > time can drastically increase page load time. First, for clients with > an empty cache, the browser must issue an HTTP request for each > resource, and incur the associated round trip times. Secondly, most > browsers prevent the rest of the page from from being loaded while a > JavaScript file is being downloaded and parsed.

These images show it more clearly why combining a number of JavaScript files into fewer output files can dramatically reduce latency:

All files are downloaded serially, and take a total of 4.46 seconds to complete All files are downloaded serially, and take a total of 4.46 seconds to complete.

After collapsing the 13 js files into 2 files: The same 729 kilobytes now take only 1.87 seconds to download The same 729 kilobytes now take only 1.87 seconds to download

Edit after Clarification given by Siku-Siku.Com: Sorry! I totally misunderstood your question. I don't know of any better way for making a particular (chunk of) script run only when the corresponding page is loaded. I think your way is good enough.

Solution 3 - Javascript

Your suggestion seems OK. I would however use an HTML 5 data- attribute to tag every page like this:

<body data-title="my_page_title">

You can then write conditional javascript code by checking this property (jQuery 1.4.3 onwards):

if ($("body").data("title") === "my_page_title") {
    // Place the logic pertaining to the page with title 'my_page_title' here...
}

This allows you to systematically group all code for a certain page in a sensible way

Solution 4 - Javascript

Another option is that you could have, within the HTML code

<script>
    callYourJSFunctionHere();  /*Things you would want to happen on Page load*/
</script>

This will follow the normal flow of the page. Therefore if you are playing with elements on the page, you will need to place this <script> portion at the bottom of the page, after all the elements have been loaded by the browser.

I'm not 100% sure this is more efficient then the current solution you have, but on the other hand, it will be telling someone looking at your HTML page what JavaScript will be run when the page loads. This might be helpful in terms of maintenance down the road.... no so much a guessing game as to what script runs when the page loads.

Hope this helps!

Solution 5 - Javascript

Ok. Lets hope, code example will say it better, than wall of text:


your one-and-only.js file:

var MY_SITE = {
    // you have one namespace-object where you hold all your stuff
    main_page: {
        // inside you have smaller chunks, for specific pages or widgets
        // this one is for main page
        _init: function () {
        ... 
        },
        ... 
        showSplashScreen: function () {
        ...
        }      
    },
    ... 
    // but there are other pages
    contacts: { ... },
    // or widgets
    cool_menu: { ... },
    // and maybe you want to put common functions into dedicated block
    global_utilities: { ... },
    // and of course - the loader
    _start: function () {
        // here we simply call one _init of the page module that was specified 
        this[PAGE_TYPE]._init(); 
        // and more tricky stuff - we can search the page for elements
        // that contain special data-attribute, collect them
        var widgets = $('[data-our-widget]').each().getAttributeValue( 'data-or-widget' );
       // and then _init each of those widgets just like we did with main page 
        widgets.forEach( function (v) {
            this[v]._init();
        }
    }
};
document.on('ready', MY_SITE.start); // here we assume some random js framework

your <script> tags on the page:

<script>var PAGE_TYPE = 'main_page';</script>
<script src="one-and-only.js" />

your 'magic' elements on the page:

<div id="whatever" data-our-widget="cool_menu"> ... </div>

Disclaimer: This is high level overview! Implementation details may and should vary, depending on your needs.

Solution 6 - Javascript

Your question is asking how to only load chunks of your scripts based upon a particular page. However, since your stated goal is minimizing http requests (and traffic, I assume), I think my unique (complicated) implementation is at least relevant to this question, if not a good alternative/complement to your approach.

I use this in an embedded application where I combine all javascript files into one large one for reducing http requests. I had to figure out a caching method as well. A 120kb javascript file in addition to several decently-sized images take a while to load when using a barebones 50kb webserver.

First off, I use YUI Compressor to compress my javascript files, as that's an easy way to save bandwidth.

A cool "feature" I discovered was if you link to anything (css, js, img) like the following, you'll simply load the file and ignore the parameters past the '?' in 'src':

<script type="text/Javascript" src="js/all.js?2382a97f099f42cc43c1b616cd24f281"></script>

Now, that seemingly random jumble of numbers and letters is actually the md5 checksum of the javascript file! I then modified my server's response header for the correlated extensions (css, js, img, etc) to never expire. This header is not sent for html files, as we always want the latest versions of those.

Cache-Control:max-age=290304000

This means clients will load the javascript file once and only once until the checksum changes. Caching works by looking up the entire request, including file parameters.

I'm actually compiling my html/js/css/image files in with C code, so I have a perl script automatically insert those md5 checksums into the html files before compiling. This obviously won't work in your case, but you can use any number of ways to handle it, as long as you have a server-side language to aid you. For example, you can have php calculate the checksum on-the-fly, or you can have php store the checksums in a database or flat file to easily look them up. In order to flag php to recalculate the checksum, you could check the filetime, or delete the database entries/flat file.

I realize this is a lot of upfront work and might not be what you're after, but it works wonderfully for me. The performance gains for me are incredible. The first page load is awfully slow (8.5 seconds), but after that, all your scripts, css, and images are all cached now. New page loads within the same application drop to ~300ms.

Solution 7 - Javascript

As you've rightly mentioned, the idea of splitting / combining files really does depend on the situation.

I don't think there can be a better solution for what you're doing, as I have seen many big websites implement a similar strategy albeit more for CSS than js.

Eg (An excerpt from facebook):

<body class="hasLeftCol home fbx hasSlimHeader safari4 win Locale_en_US defaultScrollbar">

There is one more great advantage you can reap out of this. You can selectively apply some JS code into groups of pages using the class attribute.

ie.

if $('body.pagesThatHaveThisClass').length > 0)
{.... code ...}

if $('body#singlePageWithThisId').length > 0)
{.... code ...}

Else, there is a technique that many JS/jQ based menus use to automatically give a different style to the current page. You can match the window.location to specific a address.

ie.

if (window.top.location.href == 'http://domain.com/mypage1.html')
{ .... code ....}

or even

switch (window.top.location.href)
{
   case 'http://domain.com/mypage1.html' :
    { .... code ....}
   
   case 'http://domain.com/mypage2.html' :
    { .... code ....}

}

I would still prefer your technique as you can group code for multiple pages together. The only reason I can think of for using the second method is when the JS people don't have access to the HTML of the page to change the class / id etc.

Hope this is what you were looking for!

Solution 8 - Javascript

How about testing window.location? That seems the obvious way for me.

Or as you already seem to do, test for the required elements to be there. That is a rather robust method, I see nothing wrong with that.

Solution 9 - Javascript

This is going to be a high level overview, but I recommend using Backbone to organize the logic for each "view" (or page.) I use a root element and change the class of that element to switch between the other views (or states) of the layout. You can then organize the secondary styles by class and keep a primary style under the ID.

HTML

<div id="layout" class="default">
    <!-- PUT DEFAULT HTML IN HERE -->
</div>
<script id="shareTemplate" type="text/html">
    <!-- PUT SHARE HTML IN HERE -->
</script>

CSS

#layout {
    /* primary style */
}

#layout.share {
   /* secondary style */
}

JavaScript

window.LayoutView = Backbone.View.extend({

    el: "#layout",

    shareTemplate: _.template($("#shareTemplate").html()),

    initialize: function() {
        this.render(shareTemplate, data);
        this.el.className = "share";
    },

    render: function(template, data) {
        $(this.el).html(template(data));
        return this;
    }

});

window.Layout = new LayoutView;

Solution 10 - Javascript

Based on the assumption that all of the different pages on your site is categorically or at least logically grouped into different "view models", with a different view model for a different type of page (e.g. "search results", "blogposts", "front page", "single blogpost", etcetera), you could have a single javascript file that contains the scripts in common for all of your site, and one specific for each view model (if you deem it necessary).

You could then have the server insert the name of the view model into, for instance, a HTML data-tag, for later use by JavaScript, and an additional script tag after the main script library.

For instance:

<head>
  <!--
    Two JavaScript files, one for the common functionality,
    and one for the view model specfic
  -->
  <script type="text/javascript" src="path/to/scriptslib.js"></script>
  <script type="text/javascript" src="path/to/viewmodel.front_page.js"></script>
</head>
<body data-view-model="front_page">
  ...
</body>

In scriptslib.js:

var
  GlobalSettings = {},
  ViewModel = {};

$(function () {
  // Save the view model in a variable when the body's been loaded
  GlobalSettings.viewModel = $("body").attr("data-view-model");
  // Assuming that the viewmodel JavaScript file has been loaded, call a method that it'll have by convention here
  ViewModel.initialize();
  // Additionally, you can later test for specific view models and perform necessary actions, if needed
  if (viewModel === "front_page") {
    // ...
  }
});

Then, in viewmodel.front_page.js (or any other view model file):

ViewModel.FrontPage = {
  // data and methods specific for this view model
}
ViewModel.initialize = function () {
  // do stuff specific for this view model
  this.FrontPage.someFunction();
};

Note, that the way I've described the approach here is for sites that rely on page reloads for shifting between pages. If you were using Ajax to shift between pages, you'd want JavaScript to dynamically load the new view models and execute them.

Hope this could work for you ;)

Solution 11 - Javascript

You can follow your approach, but suppose your "cocktail" javascript file has 20k lines of code, and you include it in all you html files, even those that show a "404" or "500", wouldn't it use more unnecessary bandwidth?

I opt for using different js for each HTML page and whatever is used across multiple pages dump it in a "utils.js" or whatever you want to call it, example

\www
  \pages
    \index.html
    \dashboard.html
    \account.html
    \ ...
  \scripts
    \index.js <- for index.html
    \dashboard.js <- for dashboard.html
    \account.js <- for account.html
    \ ...
    \utils.js|shared.js|sharedlib.js <- used by all pages

so basically it will result in:

  • 1 request for the html
  • 1 for the page script
  • 1 for the shared script, if used
  • n for css's, but I assume is 1

This is just my two cents..

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmoeyView Question on Stackoverflow
Solution 1 - JavascriptCharlinoView Answer on Stackoverflow
Solution 2 - JavascriptSajib MahmoodView Answer on Stackoverflow
Solution 3 - JavascriptWesleyView Answer on Stackoverflow
Solution 4 - Javascriptblo0p3rView Answer on Stackoverflow
Solution 5 - Javascriptc69View Answer on Stackoverflow
Solution 6 - JavascriptJeff LambView Answer on Stackoverflow
Solution 7 - JavascriptGaurav RamananView Answer on Stackoverflow
Solution 8 - JavascriptHas QUIT--Anony-MousseView Answer on Stackoverflow
Solution 9 - JavascriptrxgxView Answer on Stackoverflow
Solution 10 - JavascriptSune RasmussenView Answer on Stackoverflow
Solution 11 - Javascriptuser497849View Answer on Stackoverflow