function toolLinks(){
var tools = document.createElement('ul');
var item = document.createElement('li');
var itemlink = document.createElement('a');
itemlink.setAttribute('href', '#');
itemlink.appendChild(document.createTextNode('close'));
itemlink.onclick = function(){window.close();}
item.appendChild(itemlink);
tools.appendChild(item);
var item2 = document.createElement('li');
var itemlink2 = document.createElement('a');
itemlink2.setAttribute('href', '#');
itemlink2.appendChild(document.createTextNode('print'));
itemlink2.onclick = function(){window.print();}
item2.appendChild(itemlink2);
tools.appendChild(item2);
document.body.appendChild(tools);
}Forums and mailing lists are full of requests about Ajax, DOM Scripting and how to use this or that library or effect. There is also an extraordinary amount of scripts, libraries and effects being developed and showcased, and the blogs and news sites specialising in scripting hardly have time to look at the demos properly before they are on Digg.com or del.icio.us and making the rounds from these sites.
Times to celebrate for those who have hung on to their skills when the DHTML craze subsided in 2001 and JavaScript became persona non grata on your CV as a main skill.
Code that shows love to the visitor
On the whole scripts released today are a lot cleaner than those of the DHTML era - most probably because of the lack of code forking necessary with browsers these days. I am also very pleased to see that the idea of Unobtrusive JavaScript has become quite common and is regarded as a goal for aspiring developers rather than a necessary evil.
One exception might be Ajax frameworks and code generators that spit out JavaScript - which lead some ‘JavaScript evangelists’ such as Jeremy Keith to coin new terms such as HIJAX - which in essence is Ajax with progressive enhancement instead of JavaScript dependencies.
All in all, it is a great time to be in the know and enthusiastic about JavaScript. Some behaviour patterns from the old days repeat themselves though. You see a lot of code that is ‘experimental’ and ‘not fit for a live system’ getting hyped and re-used without caring about the initial concerns of the developer - sometimes even blatantly omitting these warnings when reporting about the code experiment.
You also see the old chestnut of recoding visual effects that were pretty useless in older technologies with newer technologies. An image reflected in an animated lake is a two second ‘wow’ effect and from there on a nuisance, no matter if you achieve it using Java, Flash or JavaScript.
10 PRINT ‘HELLO MAINTAINER: GOTO HELL
So we’re making life a lot easier for the visitors coming to web sites by creating scripts that are not in their way, but only help then when and if their agents support it. On the other hand, as a maintainer a lot of code flung in my direction is a pain to sift through, debug and generally find the spots where I can customise or change settings. Call me Captain Obvious to the Rescue, but isn’t it time we - knowing very well that implementing and maintaining code is annoying and causes a lot of frustration - cut future maintainers of our code the same slack?
Unobtrusive JavaScript that is easy to maintain sounds like a winner that could help us spend less time with frustrating trial and error and give us some spare time we could spend on innovating or documenting what we do.
Maintainable JavaScript? But how?
So here’s an eight step plan to show you what can be done to make your scripts easier for the maintainer. Yes, some of the steps may be very obvious to the jet-set, well travelled and seasoned JavaScript developer, but it can’t hurt to remind ourselves of their virtues.
Furthermore, I dare any one of you to look at some older code and test it against them - I am not ashamed to admit that at times I looked at code and uttered expletives about the person responsible, only to find out later on that it was in fact me a few months back. Scope and feature changes together with looming deadlines and budget cuts breed bad code; it is a fact of software development.
Step 1: Keep Your Syntax and Structure Clean and Logical
This means you indent your code, keep a line-length limit of say 80 characters and keep functions reasonably small. A rule of thumb is to keep each task in its own function rather than having one large function to do everything. As an additional benefit, this means that you can re-use these functions when you extend the script at a later stage. Don’t go the ‘IDE generated - Java’ route though and create many many many one line functions - this can become more confusing than using one massive function.
There is a holy war going on about tabs vs. spaces for indenting and curly braces on an own line or on the same of the opening command. I don’t care, as long as you keep it consistent, and also consistent with the other code, were you to extend an already existing project. As an example let’s take a function that generates ‘close’ and ‘print’ links for a popup - links that only make sense when JavaScript is available and should therefore be generated with JavaScript.
You can make this a lot more readable by indenting and separating each task out into own functions:
function toolLinks(){
var tools = document.createElement('ul');
var item = document.createElement('li');
var itemlink = createLink('#', 'close', closeWindow);
item.appendChild(itemlink);
tools.appendChild(item);
var item2 = document.createElement('li');
var itemlink2 = createLink('#', 'print', printWindow);
item2.appendChild(itemlink2);
tools.appendChild(item2);
document.body.appendChild(tools);
}
function printWindow(){
window.print();
}
function closeWindow() {
window.close();
}
function createLink(url,text,func){
var temp = document.createElement('a');
temp.setAttribute('href', url);
temp.appendChild(document.createTextNode(text));
temp.onclick = func;
return temp;
}Step 2: Use clever variable and function names
This is basic good coding practice, although you see it done badly a lot. Once again, quick fixes and looming deadlines are to blame. In essence it means that you use variable and function names that tell the maintainer immediately what this chunk of data is. You can use underscores or camelCase to concatenate different words - personally I prefer camelCase, as that is consistent with the methods JavaScript provides you with (eg. getElementsByTagName()). It might even be a good idea to use generic names for variables that are just computed inside a function or loop and don’t need maintaining like the ‘temp’ variable inside the createLink() function in the earlier example.
Take a leaf out of the book for good CSS design - make sure to keep your function and variable names generic and describe a task rather than a visual result. A createFatRedBoxForLeftNavigation() might become a createTopBarForSectionNavigation() in another code iteration but doesn’t have to if you had called it createSecondaryMenu() from the start.
It is also a very good to stick to English variable and function names - you never know if you have to share the code world wide or send it to another country for maintenance.
Step 3: Comment your code
Commenting can save a lot of time and grief. However, you can also overdo it. In tutorials and books you’ll find code that has a comment on each line explaining what is going on. This is because of the intended audience, and makes a lot of sense inside a tutorial but is rather superfluous in live code. Comments are needed when:
- There is a section of code that needs maintenance, eg. ‘Change ID names here’
- There is a section that might become outdated as it fixes a problem for user agents that are hip right now
- There is a reason why you programmed something in one way while there might be better ones and you want to explain that
- The code section is dependent on another script or gets parameters whose origin may not be obvious at first.
Step 4: Keep your scripts self-contained
This step ties in with unobtrusive JavaScript, so let’s not dwell on it. In essence it means that a maintainer could add your script to any page and don’t risk overwriting other code by doing so. You can keep scripts self-contained by namespacing them or wrapping them in an object via the object literal. You ensure you don’t hijack variables by keeping them in scope of your functions via the var keyword and you don’t hijack events by using addEvent() or its derivates.
Step 5: Keep maintained variables separate and test dependencies
This step is simple: Keep variables that need to be maintained at the beginning of the function and pre-set them with dummy values if necessary. That way the maintainer always knows where to change settings and doesn’t have to scan through the whole script and you won’t run into ‘not defined’ errors.
function toolLinks(){
var tools, closeWinItem, closeWinLink, printWinItem, printWinLink;
// link labels, please edit
var printLinkLabel = ‘print’;
var closeLinkLabel = ‘close’;#
tools = document.createElement(’ul’);
closeWinItem = document.createElement(’li’);
closeWinLink = createLink(’#', closeLinkLabel, closeWindow);
closeWinItem.appendChild(closeWinLink);
tools.appendChild(closeWinItem);
printWinItem = document.createElement(’li’);
printWinLink = createLink(’#', printLinkLabel, printWindow);
printWinItem.appendChild(printWinLink);
tools.appendChild(printWinItem);
document.body.appendChild(tools);
}
Testing for dependencies is a trait of Unobtrusive JavaScript - you test for the existence of HTML elements before you access them via DOM methods. Taking this idea further, make sure you test if there are any dependencies in your scripts and alert the maintainer if there is a problem. This is a big issue with a lot of JavaScript libraries. Scripts that rely on the Yahoo! User Interface library will throw an error when it is not available, this could be prevented by testing for it and showing an alert instead. Jquery is even worse as it seems to suppress any errors, which makes debugging a gamble.
Step 6: Separate and communicate the visuals
When it comes to Unobtrusive JavaScript there is no way around applying and removing CSS class names dynamically to and from elements. There is hardly any situation where you need to access the style collection of an object directly, lest you want to make it harder for the maintainer. Exceptions are drag and drop interfaces, but even then all you change via JavaScript is the x and y coordinates of the element. The rest of the look and feel should stay where it was meant to be: in the CSS file. This ensures that CSS designers don’t mess around with your code and you don’t need to know all the ins and outs of CSS bugs in browsers - and there are a lot more than we’d like there to be.
The most basic way to communicate the names of these classes is keeping them in variables and commenting them at the beginning of your script:
demo = {
// CSS classes
hideClass : 'hide', // hide elements
currentClass : 'current', // current navigation element
hoverClass : 'over', // hover state
[... rest of the code ...]
}
If you want to go even further in your separation then you could create an own include file called for example cssClassNames.js and use a JSON notation in this one.
css={
'hide' : 'hide', // hide elements
'current' : 'current', // current navigation element
'hover' : 'over', // hover state [... and so on for a lot of them...]
}
In your script you can reach the different class names with css.hide or css.current respectively. You could even use more natural labels like ‘hide elements’ and reach them via css ‘hide elements’, which’ll make it easier for the maintainer but harder for you as a coder.
One really easy trick is a class name to communicate with the CSS designer that you apply to the body of the document. This will allow the CSS designer to distinguish between styles that are applied to the non-JavaScript version and to the scripting enhanced version respectively.
if(!document.getElementById || !document.createElement){return;}
cssjs('add',document.body,'jsenabled');
CSS:
/* No JavaScript */
#nav li{
[...settings...]
}
/* JavaScript */
body.jsenabled #nav li{
[...settings...]
}
This is also very handy to hide a lot of elements via CSS instead of looping through all of them. In extreme cases, where you have a lot of styles and totally different settings for the scripting enabled version, you could even apply a totally different style sheet via JavaScript (either use document.write() or create a new LINK element).
Step 7: Separate textual content and code
The same tricks apply to textual content, you can either separate labels out as variables or - even better - into an own document called textContent.js in JSON format. An example that worked exceptionally well is this one:
var locales = {
'en': {
'homePageAnswerLink':'Answer a question now',
'homePageQuestionLink':'Ask a question now',
'contactHoverMessage':'Click to send this info as a message',
'loadingMessage' : 'Loading your data...',
'noQAmessage' : 'You have no Questions or Answers yet',
'questionsDefault': 'You have not asked any questions yet',
'answersDefault': 'You have not answered any questions yet.',
'questionHeading' : 'My Questions',
'answersHeading' : 'My Answers',
'seeAllAnswers' : 'See all your Answers',
'seeAllQuestions' : 'See all your Questions',
'refresh': 'refresh'
},
'fr': {
},
'de': {
}
};
It allowed the editorial staff to translate and maintain the labels in all the different languages without having to touch the code. I even managed to separate the IDs out for them to sort out with the HTML developers. All the JavaScript had to do was to read out the application’s locale setting and loop through the elements available:
function doI18N(){
var lang = yhost.PluginLocale;
for(i in locales[lang]){
if(document.getElementById(i)){
document.getElementById(i).innerHTML = locales[lang][i];
}
}
}Step 8: Document your code
Last but oh so not at all least: write documentation about your script / library / effect. Good documentation takes the audience into consideration, which means that for some libraries it is good to keep it a classic API documentation with all possible properties and parameters of methods explained in ‘techspeak’, but in general I have had much more success with ‘Explain by example, then list all possibilities’ documentation. Good documentation saves the most time and money, actually a shame we hardly get the time to create it. Other programming languages tend to automatically generate documentation from code comments (PHP for example), however that does not necessarily make for better docs, just less work.
What about high traffic sites?
If you are working in a high traffic environment, or one that only allows for small code packets (mobile market) then you might frown upon the ideas explained here as they can bloat the code. The way around that problem is to maintain a code base and a live code repository, which is generated from the original code after stripping out the comments, replacing long variable names with shorter ones and performing other packing and minifying tasks. Tools to do that for you are Douglas Crockford’s jsmin and Dean Edward’s packer.
Summary
I hope that the above examples have given you an idea how to make your code more maintainable or reminded you of some ideas that lay dormant in the back of your head. If you have other ideas, or you strongly disagree with some of the practices (and you have good reason to), please drop a comment here, as with everything that is ‘best practice’, it is a work in progress and collaboration can only make it better.
Source: thinkvitamin.com
