What we do is use JS to attach behaviours to the markup.
This keeps the JS totally separate from the markup. This makes it easier to maintain: we don't need to remember to change the JavaScript in lots of places in the markup (which isn't controlled by the Design Framework), but we can just change it in one file (which is).
So, we could completely replace the JavaScript for a particular widget in a future version of the Design Framework and the developers wouldn't have to change a thing in the markup.
See http://blog.teamtreehouse.com/unobtrusive-javascript-important for a good explanation.
There are many ways to organise JavaScript code. This is the pattern we've ended up using.
Read more about it here:
The main advantage is that all of our functions and variables can be "namespaced" so they don't clash with other functions and variables. It can also be clearer to see which functions we've made "public" so that other developers can call them.
JavaScript functions and variables are global by default, which means anything can change them at any time.
This can have unfortunate consequences.
For example, open a browser console and type the following:
alert('hello')
and press return. A nice little popup should appear.
Now, what if some bit of JavaScript somewhere wanted to define an alert message and store it in a variable. It could do this:
alert = "My lovely little alert message."
That would completely overwrite the alert() function we used before. Try typing that code into the console and then try running alert('hello')
again. The error message will tell us that alert()
is no longer a function, which is entirely true: we've just made it a string of text.
So, we need to be careful. A module pattern helps us wrap up our functions and variables so they don't overwrite other bits of code.
This is what happens when a standard page using the Design Framework loads (things are a bit different for an SPA, which we'll come to in a bit).
init()
function and runs that, which does things like preparing accordions and tabs so they're ready to be used.A simple example:
/**
* Initialise.
*/
function init () {
checkForAdverts()
updateBodyAttributes()
}
With a single page app, we don't have all the HTML on the page to start with, so there's no point in calling all the init()
functions once. Instead, developers have to call the appropriate init()
when adding something to the page. For example, they might add an accordion and there need to call the accordion init()
.
By setting a context we can limit where our code looks for things to act on. We don't always need to do this, but often it's useful.
For example, when adding accordions to an already-loaded page, the developer may want to initialise only the new accordions in a specific area of the page. By passing a context, they can do this.
/**
* Initialise.
*/
function init (context) {
context = context || document
doThatAccordionSetupThing(context)
}
In this example, the line context = context || document
sets document as the default context if the developer hasn't specified something.
There is not often a need for this, but it's something that needs to be considered: do we need to clean up after ourselves?
Because the JavaScript in the Design Framework is used in all sorts of applications, it makes sense to keep it as lightweight as possible. While jQuery makes some things easier, we don't need lots of its browser-support and most of the code we write is very simple stuff. Removing a need for jQuery means less code needs to be downloaded.
Like lots of other folks we use JavaScript Standard Style for our code formatting. There's details in the Design Framework README on how to set it up.
Using this standard style makes it easier to review code and understand what other people have written.
/**
* A stand-in for forEach until such time we can use it.
* @public
* @param {array} a - the array.
* @param {function} fn - the callback.
* @return {object} context - the context or scope.
*/