If we write all our JavaScript as methods on the window object, we'll end up in a mess.
Using objects to group related code and immediately called functions to create a private variable scope helps avoid conflicts.
Initialising the right scripts
Lots of JavaScript files means lots of HTTP requests, which is slow.
Running lots of unneeded JavaScript setup on every page can also be slow, e.g. why initialise inline form validation on a page with no forms?
It is good to serve all of the JavaScript as a single file to reduce HTTP requests and benefit from caching, but be selective about which scripts we run.
This doesn't mean we should write the JavaScript in one big file (more on this later).
Ask the HTML what it needs
<body class='with-js-image-gallery'>
Because the purpose of the JavaScript is to enhance an HTML document, it makes sense to ask the document what it has that needs enhancing.
Class names on the body keeps everything in one place.
We have to write defensive init functions so we don't enhance the same bit of markup twice and potentially break our pages, e.g. if the JavaScript adds some markup to the page we don't want it to be added twice.
Adding a class to enhanced markup also gives us a convenient way of targeting CSS rules which are only appropriate when the JavaScript enhancements have been applied (it's good to keep style rules out of JavaScript, in the same way that it's good to keep JavaScript and CSS out of HTML).
Note that if you always use the same class name (e.g. enhanced) you can't initialise two features on the same element (thanks to the person who pointed this out in the Q&A at RubyConfUA).
This all sounds like a lot of work
We've talked about adding classes to the body, and setting custom HTTP headers.
Why not roll all of this into some handy server-side helpers?
Requesting JavaScript
def use_javascript_for(feature)
@js_features ||= []
@js_features << feature.to_s.downcase if request and request.xhr?
header = @js_features.uniq.join(' ')
response.headers['X-With-JavaScript'] = header
end
end
Calling use_javascript_for in our views allows bits of HTML to request specific JavaScript enhancment.
Generating the HTTP headers and body classes in one place means we can load render a partial as part of a view or load it via XHR and the JavaScript will just work.