Website Design Edmonton       Website Design Feedback       Website Design Sitemap     Contact Website Design Company
Website Designers R Us - Home
From DHTML to DOM Scripting

Code Layout

First and foremost, code is there to be converted by the interpreter to make a computer do something--or at least this is a very common myth. The interpreter will swallow the code without a hiccup when the code is valid--however, the real challenge for producing really good code is that a human will be able to edit, debug, amend, or extend it without spending hours trying to figure out what you wanted to achieve. Logical, succinct variable and function names are the first step to make it easier for the maintainer--the next one is proper code layout.

Note If you are really bored, go to any coder forum and drop an absolute like "spaces are better than tabs" or "every curly brace should get a new line." You are very likely to get hundreds of posts that point out the pros and cons of what you claimed. Code layout is a hotly discussed topic. The following examples work nicely for me and seem to be a quite common way of laying out code. It might be a good idea to check whether there are any contradictory standards to follow before joining a multideveloper team on a project and using the ones mentioned here.

Simply check the following code examples; you might not understand now what they do (they present a small function that opens every link that has a CSS class of smallpopup in a new window and adds a message that this is what will happen), but just consider which one would be easier to debug and change?

Without indentation:

function addPopUpLink(){
if(!document.getElementById||!document.createTextNode){return;}
var popupClass='smallpopup';
var popupMessage= '(opens in new window)';
var pop,t;
var as=document.getElementsByTagName('a');
for(var i=0;i<as.length;i++){
t=as[i].className;
if(t&&t.toString().indexOf(popupClass)!=-1){
as[i].appendChild(document.createTextNode(popupMessage));
as[i].onclick=function(){
pop=window.open(this.href,'popup','width=400,height=400');
returnfalse;
}}}}
window.onload=addPopUpLink;
With indentation:
function addPopUpLink(){
  if(!document.getElementById || !document.createTextNode){return;}
  var popupClass='smallpopup';
  var popupMessage= ' (opens in new window)';
  var pop,t;
  var as=document.getElementsByTagName('a');
  for(var i=0;i<as.length;i++){
    t=as[i].className;
    if(t && t.toString().indexOf(popupClass)!=-1){
      as[i].appendChild(popupMessage);
      as[i].onclick=function(){
        pop=window.open(this.href,'popup','width=400,height=400');
        return false;
      }
    }
  }
}
window.onload=addPopUpLink;

With indentation and curly braces on new lines:

  function addPopUpLink()
  {
    if(!document.getElementById || !document.createTextNode){return;}
    var popupClass='smallpopup';
    var popupMessage= ' (opens in new window)';
    var pop,t;
    var as=document.getElementsByTagName('a');
    for(var i=0;i<as.length;i++)
    {
      t=as[i].className;
      if(t && t.toString().indexOf(popupClass)!=-1)
      {
        as[i].appendChild(document.createTextNode(popupMessage));
        as[i].onclick=function()
        {
          pop=window.open(this.href,'popup','width=400,height=400');
          return false;
        }
      }
    }
  }
  window.onload=addPopUpLink;

I think it is rather obvious that indentation is a good idea; however, there is a big debate whether you should indent via tabs or spaces. Personally, I like tabs, mainly because they are easy to delete and less work to type in. Developers that work a lot on very basic (or pretty amazing, if you know all the cryptic keyboard shortcuts) editors like vi or emacs frown upon that, as the tabs might display as very large horizontal gaps. If that is the case, it is not much of a prob-lem to replace all tabs with double spaces with a simple regular expression.

The question of whether the opening curly braces should get a new line or not is another you need to decide for yourself. The benefit of not using a new line is that it is easier to delete erroneous blocks, as they have one line less. The benefit of new lines is that the code does look less crammed. Personally, I keep the opening one on the same line in JavaScript and on a new line in PHP--as these seem to be the standard in those two developer communities.

Another question is line length. Most editors these days will have a line-wrap option that will make sure you don't have to scroll horizontally when you want to see the code. However, not all of them print out the code properly, and there may be a maintainer later on that has no fancy editor like that one. It is therefore a good idea to keep lines short--approximately 80 characters.

Commenting

Commenting is something that only humans benefit from--although in some higher program-ming languages, comments are indexed to generate documentation (one example is the PHP manual, which is at times a bit cryptic for nonprogrammers exactly because of this). While commenting is not a necessity for the code to work--if you use clear names and indent your code, it should be rather self-explanatory--it can speed up debugging immensely. The previ-ous example might make more sense for you with explanatory comments:

/*
  addPopUpLink
  opens the linked document of all links with a certain
  class in a pop-up window and adds a message to the
  link text that there will be a new window
*/
function addPopUpLink(){
  // Check for DOM and leave if it is not supported
  if(!document.getElementById || !document.createTextNode){return;}
  // Assets of the link - the class to find out which link should
  // get the functionality and the message to add to the link text
  var popupClass='smallpopup';
  var popupMessage= ' (opens in new window)';
  // Temporary variables to use in a loop
  var pop,t;
  // Get all links in the document
  var as=document.getElementsByTagName('a');
  // Loop over all links
  for(var i=0;i<as.length;i++)
  {
    t=as[i].className;
    // Check if the link has a class and the class is the right one
    if(t && t.toString().indexOf(popupClass)!=-1)
    {
      // Add the message
      as[i].appendChild(document.createTextNode(popupMessage));
      // Assign a function when the user clicks the link
      as[i].onclick=function()
      {
        // Open a new window with
        pop=window.open(this.href,'popup','width=400,height=400');
        // Don't follow the link (otherwise the linked document
        // would be opened in the pop-up and the document).
        return false;
      }
    }
  }
}
window.onload=addPopUpLink;

A lot easier to grasp, isn't it? It is also overkill. An example like this can be used in training documentation or a self-training course, but it is a bit much in a final product--moderation is always the key when it comes to commenting. In most cases, it is enough to explain what something does and what can be changed.

/*
  addPopUpLink
  opens the linked document of all links with a certain
  class in a pop-up window and adds a message to the
  link text that there will be a new window
*/
function addPopUpLink()
{
  if(!document.getElementById || !document.createTextNode){return;}
  // Assets of the link - the class to find out which link should
  // get the functionality and the message to add to the link text
  var popupClass='smallpopup';
  var popupMessage=document.createTextNode(' (opens in new window)');
  var pop,t;
  var as=document.getElementsByTagName('a');
  for(var i=0;i<as.length;i++)
  {
    t=as[i].className;
    if(t && t.toString().indexOf(popupClass)!=-1)
    {
      as[i].appendChild(popupMessage);
      as[i].onclick=function()
      {
        pop=window.open(this.href,'popup','width=400,height=400');
        return false;
      }
    }
  }
}
window.onload=addPopUpLink;

These comments make it easy to grasp what the whole function does and to find the spot where you can change some of the settings. This makes quick changes easier--changes in functionality would need the maintainer to analyze your code more closely anyway.

Functions

Functions are reusable blocks of code and are an integral part of most programs today, including those written in JavaScript. Imagine you have to do a calculation or need a certain conditional check over and over again. You could copy and paste the same lines of code where necessary; however, it is much more efficient to use a function.

Functions can get values as parameters (sometimes called arguments) and can return values after they finished testing and changing what has been given to them. You create a function by using the function keyword followed by the function name and the parameters separated by commas inside parentheses:

function createLink(linkTarget, LinkName)
{
  // Code
}

There is no limit as to how many parameters a function can have, but it is a good idea not to use too many, as it can become rather confusing. If you check some DHTML code, you can find functions with 20 parameters or more, and remembering their order when calling those in other functions will make you almost wish to simply write the whole thing from scratch. When you do that, it is a good idea to remember that too many parameters mean a lot more maintenance work and make debugging a lot harder than it should be.

Unlike PHP, JavaScript has no option to preset the parameters should they not be available. You can work around this issue with some if conditions that check whether the parameter is null (which means "nothing, not even 0" in interpreter speak):

function createLink(linkTarget, LinkName)
{
  if (linkTarget == null)
  {
    linkTarget = '#';
  }
  if (linkName == null)
  {
    linkName = 'dummy';
  }
}

Functions report back what they have done via the return keyword. If a function that's invoked by an event handler returns the Boolean value false, then the sequence of events that is normally triggered by the event gets stopped. This is very handy when you want to apply functions to links and stop the browser from navigating to the link's href. We also used this in the "Object Detection vs. Browser Dependence" section. Any other value following the return statement will be sent back to the calling code. Let's change our createLink function to create a link and return it once the function has finished creating it.

function createLink(linkTarget,linkName)
{
  if (linkTarget == null) { linkTarget = '#'; }
  if (linkName == null) { linkName = 'dummy'; }
  var tempLink=document.createElement('a');
  tempLink.setAttribute('href',linkTarget);
  tempLink.appendChild(document.createTextNode(linkName));
  return tempLink;
}

Another function could take these generated links and append them to an element. If there is no element ID defined, it should append the link to the body of the document.

function appendLink(sourceLink,elementId)
{
  var element=false;
  if (elementId==null || !document.getElementById(elementId))
  {
    element=document.body;
  }
  if(!element) {
    element=document.getElementById(elementId);
  }
  element.appendChild(sourceLink);
}

Now, to use both these functions, we can have another one call them with appropriate parameters:

function linksInit()
{
  if (!document.getElementById || !document.createTextNode) { return; }
  var openLink=createLink('#','open');
  appendLink(openLink);
  var closeLink=createLink('closed.html','close');
  appendLink(closeLink,'main');
}
 The function linksInit() checks whether DOM is available (as it is the only function calling the others, we don't need to check for it inside them again) and creates a link with a target of # and open as the link text. It then invokes the appendLink() function and sends the newly generated link as a param-eter. Notice it doesn't send a target element, which means elementId is null and appendLink() adds the link to the main body of the document. 
The second time initLinks() invokes createLink(), it sends the target closed.html and close as the link text and applies the link to the HTML element with the ID main via the appendLink() function. If there is an element with the ID main, appendLink() adds the link to this one; if not, it uses the document body as a fallback option. 
If this is confusing now, don't worry, you will see more examples later on. For now, it is just important to remember what functions are and what they should do: 
Functions are there to do one task over and over again--keep each task inside its own function; don't create monster functions that do several things at once. 
Functions can have as many parameters as you wish, and each parameter can be of any type--string, object, number, variable, or array.
You cannot predefine parameters in the function definition itself, but you can check whether they were defined or not and set defaults with an if condition. You can do this very succinctly via the ternary operator, which you will get to know in the next section of this chapter.
Functions should have a logical name describing what they do; try to keep the name close to the task topic, as a generic init(), for example, could be in any of the other included JavaScript files and overwrite their functions.
Object literals can provide one way to avoid this problem, as you'll see later in this chapter.
Short Code via Ternary Operator
Looking at the appendLink() function shown earlier, you might get a hunch that a lot of if conditions or switch statements can result in very long and complex code. A trick to avoid some of the bloating involves using something called the ternary operator. The ternary operator has the following syntax:
var variable = condition ? trueValue:falseValue;
This is very handy for Boolean conditions or very short values. For example, you can replace this long if condition with one line of code: 
// Normal syntax
var direction;
If(x<200)
{
  direction=1;
}
else
{
  direction=-1
}
// Ternary operator
var direction = x < 200 ? 1 : -1;
Other examples: 
t.className = t.className == 'hide' ? '' : 'hide';
var el = document.getElementById('nav')
       ? document.getElementById('nav')
       : document.body;
You can also nest the ternary selector, but that gets rather unreadable:
y = x <20 ? (x > 10 ? 1 : 2) : 3;
// equals
if(x<20)
{
  if(x>10)
  {
    y=1;
  }
  else
  {
    y=2;
  }
}
else
{
 y=3
}
Sorting and Reuse of Functions
If you have a large number of JavaScript functions, it might be a good idea to keep them in separate .js files and apply them only where they are needed. Name the .js files according to what the functions included in them do, for example, formvalidation.js or dynamicmenu.js. This has been done to a certain extent for you, as there are a lot of prepackaged JavaScript libraries (collections of functions and methods) that help create special functionality. We will look at some of them in Chapter 11 and create our own during the next few chapters.
Variable and Function Scope
Variables defined inside a function with a new var are only valid inside this function, not outside it. This might seem a drawback, but it actually means that your scripts will not interfere with others--which could be fatal when you use JavaScript libraries or your own collections. Variables defined outside functions are called global variables and are dangerous. We should try to keep all our variables contained inside functions. This ensures that our script will play nicely with other scripts that may be applied to the page. Many scripts use generic variable names like navigation or currentSection. If these are defined as global variables, the scripts will override each other's settings. Try running the following function to see what omitting a var keyword can cause:
<script type="text/javascript">
  var demoVar=1 // Global variable
  alert('Before withVar demoVar is' +demoVar);
  function withVar()
  {
    var demoVar=3;
  }
  withVar();
  alert('After withVar demoVar is' +demoVar);
  function withoutVar()
  {
    demoVar=3;
  }
  withoutVar();
  alert('After withoutVar demoVar is' +demoVar);
</script>
While withVar keeps the variable untouched, withoutVar changes it:
Before withVar demoVar is 1
After withVar demoVar is 1
After withoutVar demoVar is 3
Keeping Scripts Safe with the Object Literal
Earlier we talked about keeping variables safe by defining them locally via the var keyword. The reason was to avoid other functions relying on variables with the same name and the two functions overwriting each other's values. The same applies to functions. As you can include several JavaScripts to the same HTML document in separate script elements your functionality might break as another included document has a function with the same name. You can avoid this issue with a naming convention, like myscript_init() and myscript_validate() for your functions. However, this is a bit cumbersome, and JavaScript offers a better way to deal with this in the form of objects.
You can define a new object and use your functions as methods of this object--this is how JavaScript objects like Date and Math work. For example:
<script type="text/javascript">
  myscript=new Object();
  myscript.init=function()
  {
    // Some code
  };
  myscript.validate=function()
  {
   // Some code
  };
</script>
Notice that if you try to call the functions init() and validate(), you get an error, as they don't exist any longer. Instead, you need to use myscript.init() and myscript.validate(). Wrapping all your functions in an object as methods is analogous to the programming classes used by some other languages such as C++ or Java. In such languages, you keep functions that apply to the same task inside the same class, thus making it easier to create large pieces of code without getting confused by hundreds of functions. 
The syntax we used is still a bit cumbersome, as you have to repeat the object name over and over again. There is a shortcut notation called the object literal that makes it a lot easier. The object literal has been around for a long time but has been pretty underused. It is becoming more and more fashionable nowadays, and you can pretty much presume that a script you find on the web using it is pretty good, modern JavaScript. What the object literal does is use a shortcut notation to create the object and apply each of the functions as object methods instead of standalone functions. Let's see our three functions of the dynamic links example as a big object using the object literal:
var dynamicLinks={
  linksInit:function()
  {
    if (!document.getElementById || !document.createTextNode)
       { return; }
    var openLink=dynamicLinks.createLink('#','open');
    dynamicLinks.appendLink(openLink);
    var closeLink=dynamicLinks.createLink('closed.html','close');
    dynamicLinks.appendLink(closeLink,'main');
  },
  createLink:function(linkTarget,linkName)
  {
    if (linkTarget == null) { linkTarget = '#'; }
    if (linkName == null) { linkName = 'dummy'; }
    var tempLink=document.createElement('a');
    tempLink.setAttribute('href',linkTarget);
    tempLink.appendChild(document.createTextNode(linkName));
    return tempLink;
  },
  appendLink:function(sourceLink,elementId)
  {
    var element=false;
    if (elementId==null || !document.getElementById(elementId))
    {
      element=document.body;
    }
    if(!element){element=document.getElementById(elementId)}
    element.appendChild(sourceLink);
  }
}
window.onload=dynamicLinks.linksInit;
As you can see, all the functions are contained as methods inside the dynamicLinks object, which means that if we want to call them, we need to add the name of the object before the function name. The syntax is a bit different; instead of placing the function keyword before the name of the function, we add it behind the name preceded by a colon. Additionally, each closing curly brace except for the very last one needs to be followed by a comma. If you want to use variables that should be accessible by all methods inside the object, you can do that with syntax that is quite similar: 
var myObject=
{
  objMainVar:'preset',
  objSecondaryVar:0,
  objArray:['one','two','three'],
  init:function(){},
  createLinks:function(){},
  appendLinks:function(){}
}
This might be a lot right now, but don't worry. This chapter is meant as a reference for you to come back to and remind you of a lot of good practices in one place. We will continue in the next chapter with more tangible examples, and rip open an HTML document to play with the different parts. 
Summary
You have done it; you finished this chapter, and you should now be able to separate modern and old scripts when you see them on the Web. Older scripts are likely to
Use a lot of document.write().
Check for browsers and versions instead of objects.
Write out a lot of HTML instead of accessing what is already in the document.
Use proprietary DOM objects like document.all for MSIE and document.layers for Netscape Navigator.
Appear anywhere inside the document (rather in the <head> or included via <script src="---.js">) and rely on javascript: links instead of assigning events.
You've learned about putting JavaScript into stand-alone .js documents instead of embedding it into HTML and thereby separating behavior from structure. You then heard about using object detection instead of relying on browser names and what progressive enhancement means and how it applies to web development. Testing user agent capabilities instead of names and versions will ensure that your scripts also work for user agents you might not have at hand to test yourself. It also means that you don't have to worry every time there is a new version of a browser out--if it supports the standards, you'll be fine.
We talked about accessibility and what it means for JavaScript, and you got a peek at a lot of coding practices. The general things to remember are
Test for the objects you want to use in your scripts. 
Make improvements in an existing site that already works well without client-side scripting instead of adding scripts first and adding nonscripting fallbacks later on.
Keep your code self-contained and don't use any global variables that might interfere with other scripts.
Code with the idea in mind that you will have to hand this code over to someone else to maintain. This person might be you in three months' time, and you should be able to immediately understand what is going on.
Comment your code's functionality and use readable formatting to make it easy to find bugs or change the functions.
This is the lot--except for something called an event handler, which I've talked about but not actually defined. I'll do so in Chapter 5. But for now, sit back, get a cup of coffee or tea, and relax for a bit, until you're ready to proceed with learning how JavaScript interacts with HTML and CSS.
Return to Listing

Website Designer R Us is one the leading website design companies on the internet. Based out of Toronto, Canada the company has dsigned and developed more than a 1000 websites worldwide.
 Home          ::             About Us          ::             Support            ::             Services            ::             Link Partners          ::             Contact

Copyright © 2006-2011 Website Designers R Us, a DOT Specialist Company. All rights reserved.