Effective JavaScript Organization for Small-Scale Sites

js-component-screenshotThere is a lot of discussion online about organizing JavaScript into modules with lazy-load capabilities and auto-require chains. This level of organization can be very valuable in web applications and even in large scale web sites – but in many websites solutions like Require.js are simply too much and introduce a level of complexity that may be difficult to maintain in the future.

I think it’s important to keep JavaScript organized and maintainable – but it can be achieved in a simpler manner for small-scale sites.

Organization Overview

Most JavaScript on a small-scale site is comprised of 3 things:

  • jQuery
  • jQuery Plugins
  • Supporting JS that uses jQuery and jQuery Plugins.

Each of these has it’s own details and special items of note.

jQuery

For many sites, jQuery is almost a given as it provides some nice behavior wrappers and reduces the complexity of cross-browser support. However, it is fairly big. The conventional wisdom (which I heartily subscribe to) is to load jQuery from a CDN with a local fallback. This local fallback is intended for times when you may be developing without a network connection – not necessarily to protect against a CDN failure.

Some key things to note from the code:

  • There is no protocol on the CDN script src attribute – this is so that it works for both HTTP and HTTPS without generating a warning under SSL.
  • The local jQuery is only loaded if the CDN jQuery isn’t available.
  • We are not including the “latest” jQuery. You should always load a specific version of jQuery, not “latest”. If you simply load “latest”, you’ll automatically get each upgrade and it may break your site.

jQuery Plugins

Plugins are a great way to extend jQuery’s reach but each additional HTTP request for a JS file has an impact on your site’s performance. The best way to handle plugins is to concatenate minified versions of the files into a single plugins.js file. This can be done either manually or via a tool like Grunt leveraging Rigger. I’ll cover this sort of a setup in a future blog post.

Supporting JS (main.js or script.js)

Here is the core of our JavaScript organization. We’ll start with a code snippet, then dissect it.

Wrapped in a Closure

We wrap the code in a closure to protect it from any other variables that may be setup by 3rd party integrations. Also, this lets us pass in external objects (like jQuery) that might be running in noConflict mode.

Components as Objects

We use a simple JavaScript object to model the component. This lets us easily organize the component into logical components with namespaced methods.

Init then Setup

The init function is always called first and is responsible for checking conditions and setting up the component if it is required. Remember, this code will be loaded on every page, but we don’t necessarily want to run the component on each page. The init function acts like a gatekeeper and only runs the setup function if it’s needed on the page

Separate Event Handler from Action

By separating the handler that responds to the click from the action we should take after the click, our code is more extensible. If you realize that you need to call that action as part of the setup, you can easily call the actionForClick function without worrying about any odd behavior stemming from an event handler.

Extend as Necessary

Simply add more elements to the object to extend the functionality. You can even setup some keys pointing to scalars or arrays to track component state.

Init is a Function

We can easily use jQuery’s onDOMReady shortcut and just pass in the init function. Similarly we could use the on body load with a slightly different setup. Be aware though, in the init function “this” isn’t SomeSiteComponent, it’s a context received from jQuery. If you want “this” to be SomeSiteComponent, your init call needs to look like this:

Caching and Compression

As discussed in Improving the Performance of your Website you still definitely want to enable caching and compression of the JavaScript. Since your main.js file may change somewhat frequently, you’ll need to include a cache-busting solution in your setup to facilitate the changes.

In Closing

This is a simple approach to organizing JavaScript into components. I’ve had great success with this approach over the years and it makes it easy to group specific behavior into a single place.