The Seven Deadly Sins Of JavaScript Implementation

Advertisement

Using JavaScript has become increasingly easy over the last few years. Whereas back in the day we needed to know the quirks of every browser, now many libraries such as jQuery, YUI, Dojo and MooTools allow someone who doesn’t even know JavaScript to spruce up boring HTML documents with impressive and shiny effects. By piggy-backing on the CSS selector engine, we have moved away from the complexity and inconsistencies of the DOM and made things much easier.

If you look at some of the code that has been released, though, we do seem to have taken a step backwards. In gaining easier access, we also became a bit sloppy with our code. Finding clearly structured, easy-to-maintain jQuery code is quite tough, which is why many plug-ins do the same thing. Writing one yourself is faster than trying to fathom what other developers have done.

The rules for solid, maintainable and secure JavaScript haven’t changed, though. So, let’s run through the seven sins of JavaScript development that will bite you in the backside when you have to maintain the code later on or hand it over to another party.

We’ve all had to work with code written by other people. We have despaired over the lack of maintainability and documentation as well as weird logic. Funny enough, as developers, we started to see this as normal and got used to ignoring other people’s work and instead writing new code for the same problems over and over, as if we were subconsciously trying to secure our jobs by leaving behind unmaintainable code—code that only we understood, while complaining that no good solutions were out there.

Sins Of Our Fathers: Browser-Specific Code

One of the main obstacles that kept us from evolving as developers was that JavaScript was largely browser-specific.

This was mainly because browsers did not support the standards (or were shipped before the governing bodies agreed on standards at all), and because we had to deliver our work before the competition and without extending the overly optimistic deadline set by our project managers.

This happens to be one reason why Internet Explorer 6 refuses to die. Hundreds of expensive software packages that are being used in offices worldwide were built when this browser was state of the art. This—and the monoculture that advocated using one software vendor for everything from the operating system to documents to spreadsheets to the browser—is the reason why companies now can’t simply discontinue support for it. It also means that newer versions of IE will always have to support the rendering mistakes of IE6 in one way or another. IE6 is the Frankenstein of the Internet, haunting its creators, terribly misunderstood by the townsfolk, who would sooner kill it, burn it and dance around it than make any sense of it.

The good news is that you won’t find many scripts these days that begin with if(document.all){} and continue with else if(document.layers){}. If you do find one, please send its creator a brief email encouraging them to move on or, better yet, to redirect their website to a better script that is actually being maintained.

Libraries to the Rescue

The job of JavaScript libraries such as jQuery, YUI, MooTools, Dojo and Glow is to make JavaScript development predictable and to relieve developers of the living hell that we call browser support. In other words, they fix random bugs in browsers and free us to adopt standards without worrying that certain browsers won’t recognize them.

For example, the DOM method getElementById(id) should be straightforward: find the element with the ID id and return it. But because some versions of IE and Opera also return elements that have the name attribute of id, jQuery solves the problem this way1:

var elem;

elem = document.getElementById( match[2] );

if ( elem ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id !== match[2] ) {
return rootjQuery.find( selector );
}

// Otherwise, we inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;
}

This is where libraries are awfully useful and is why JavaScript libraries are here to stay. Browsers will always do things wrong, and old browsers will not be upgraded by end users, either because of the aforementioned company regulations or because people simply don’t care to keep up with the times.

So, while the practice of building software for certain browsers is on the decline (at least for JavaScript—with CSS, we have a whole other headache ahead of us), we still have to be mindful of certain sins.

Sin #1: Not Playing Nice With Other Scripts

Here’s the first one, which we still see a lot of on the Web. Sadly, it is very common in demo code for APIs and Web services: global variables, functions and DOM-1 event handlers.

What do I mean by these? Consider the following:

  • Every script in the HTML document has the same rights as the others and can, if need be, overwrite what other scripts have done before.
  • If you define a variable or function name, and some other include uses the same name, the initial one will be overwritten.
  • The same applies to event handlers if you attach them the old-school onEvent way.

Say you have the script script_one.js:

x = 5;
function init(){
  alert('script one init');
  document.getElementsByTagName('h1')[0].onclick = function(){
    this.style.background = 'blue';
  }
}
alert('x is '+x);
window.onload = init;

And immediately after this one, you include another script, script_two.js:

x = 10;
  function init(){
    alert('script two init');
    document.getElementsByTagName('h1')[0].onclick = function(){
      this.style.color = 'white';
    }
  }
  alert('x is '+x);
  window.onload = init;

If you open this document in a browser2, you will find that x turns from 5 to 10 and that the first init() is never called. The script two init alert() does not come up, nor does the h1 get a blue background when you click it. Only the text turns to white, which renders it invisible.

The solution is not to use onEvent handlers, but rather the proper DOM level 2 event handlers (they don’t work in IE, but let’s not worry about that at the moment—remember, this is what libraries are for). Furthermore, wrap your functions in another with a more unique name to prevent them from overriding each other.

var scriptOne = function(){
  var x = 5;
  function init(){
    alert('script one init');
    document.getElementsByTagName('h1')[0].addEventListener(
      'click',
      function(e){
        var t = e.target;
        t.style.background = 'blue';
      },
      false
    );
  }
  alert('x inside is '+x);
  return {init:init};
}();
window.addEventListener('load',scriptOne.init,false);
alert('x outside is '+x);

var scriptTwo = function(){
  var x = 10;
  function init(){
    alert('script two init');
    document.getElementsByTagName('h1')[0].addEventListener(
      'click',
      function(e){
        var t = e.target;
        t.style.color = 'white';
      },
      false
    );
  }
  alert('x inside is '+x);
  return {init:init};
}();
window.addEventListener('load',scriptTwo.init,false);
alert('x outside is '+x);

If you run this in a browser (not Internet Explorer 6)3, everything will come up as you expect: x is first 5, then 10 on the inside, and the heading turns blue and white when you click it. Both init() functions are called, too.

You also get an error. Because x is not defined outside the functions, the alert('x outside is '+x); never works.

The reason is that by moving the x into the scriptOne and scriptTwo functions and adding the var keyword in front of them, we have made them a part of those functions but hid them from the outside world. This is called a closure and is explained in detail here4. It is probably the most powerful feature of JavaScript.

Using closures and var keywords, you won’t have the problem of variables with similar names overriding each other. This also applies in jQuery: you should namespace your functions5.

This can be tough to grasp, so let’s look at a simpler example:

var x = 4;
var f = 3;
var me = 'Chris';
function init(){}
function load(){}

All of these are global variables and functions now. Any other script having the same variables will override these.

You can nest them in an object to avoid this:

var longerAndMoreDistinct = {
  x : 4,
  f : 3,
  me : 'Chris',
  init : function(){},
  load : function(){}
}

That way, only the longerAndMoreDistinct is global. If you want to run this function, you now have to call longerAndMoreDistinct.init() instead of init(). You can reach me as longerAndMoreDistinct.me and so on.

I don’t like this because I have to switch from one notation to another. So, we can do the following:

var longerAndMoreDistinct = function(){
  var x = 4;
  var f = 3;
  var me = 'Chris';
  function init(){}
  function load(){} 
}();

You define longerAndMoreDistinct as the outcome of a function without a name that gets immediately executed (this is the () on the last line). This now means that all of the variables and functions inside exist only in this world and cannot be accessed from outside at all. If you want to make them accessible from outside, you need to return them to the outside world:

var longerAndMoreDistinct = function(){
  var x = 4;
  var f = 3;
  var me = 'Chris';
  function load(){}
  return {
    init:function(){}
  } 
}();

Now init() is available as longerAndMoreDistinct.init() again. This construct of wrapping things in an anonymous function and returning some of them is called the Module pattern6, and it keeps your variables safe. Personally, I still hate the shift in syntax, so I came up with the revealing module pattern7. Instead of returning the real function, all I do is return a pointer to it:

var longerAndMoreDistinct = function(){
  var x = 4;
  var f = 3;
  var me = 'Chris';
  function load(){}
  function init(){}
  return {
    init:init
  } 
}();

This way, I can make things either available or not available simply by adding to the object that is returned.

If you don’t need to give anything to the world and just want to run some code and keep all of your variables and function names safe, you can dispense with the name of the function:

(function(){
  var x = 4;
  var f = 3;
  var me = 'Chris';
  function load(){}
  function init(){}
})();

Using var and wrapping code in this construct makes it inaccessible to the outside world, but still makes it execute.

You may find this to be complex stuff, but there is a good way to check your code. JSLint8 is a validator for JavaScript, much like the HTML or CSS validators, and it tells you all the things that might be wrong with your code.

Sin #2: Believing Instead Of Testing

The next big sin related to implementing JavaScript is expecting everything to go right: every parameter being in the right format, every HTML element you try to enhance being truly available, and every end user entering information in the right format. This will never be the case, and that last assumption is an especially bad one because it allows malicious users to inject dangerous code.

When you write JavaScript and give it to the world or integrate it in a product that will be maintained by a third party, a little paranoia is a good thing.

typeof is your friend. Regular expressions are your friend. indexOf(), split and length are your friends. In other words, do everything you can to make sure that incoming data is the right format.

You will get a lot of errors with native JavaScript; if you do anything wrong, you’ll know what happened. The annoying thing about most JavaScript libraries is that when they fail to execute some functionality, they do it silently. The maintainer is left guessing and has to run through all the code and start debugging with stop points (or—shudder!—alerts()) to reverse-engineer where you entered instable code. To avoid this, simply wrap whatever you can in a test case rather than try to access it.

Sin #3: Using The Wrong Technology For The Job

The biggest problem with JavaScript happens when you use the wrong tool for the job. It makes maintenance a nightmare and deteriorates the code’s quality. Use tools for the jobs they were meant for. This means:

  • Absolutely essential content and mark-up should be in HTML, regardless of the environment it will be displayed in.
  • Any “look and feel” elements should be maintainable through CSS. You should not have to scour through JavaScript to change a color.
  • Any interaction with the user that goes beyond hover effects (which, by definition, are an invitation to interact and not the interaction itself—because they are inaccessible to keyboard users) should be done with JavaScript.

The main reason why this is still a valid, pragmatic and sensible approach to development is that as Web technologies get muddled (for example, you can create content with CSS and JavaScript, animate and transform in CSS and—if you really want—paint with HTML), people’s skills and interests in these different technologies vary quite a bit.

Semantic mark-up buffs are not much interested in applying closures in JavaScript. JavaScript developers are not much interested in the order of elements in CSS. And CSS fans aren’t keen to learn how to make a JavaScript animation run flicker-free.

This results in the same problems being solved over and over again, only with different technologies. This is a market-wide problem: a lot of state-of-the-art Canvas tricks were done in Flash years ago, their impact debated and their problems fixed.

My favorite instance of this is when people write loops to hide a lot of elements on the page to make them available later on.

Say this is your HTML:

<h2>Section 1</h2>
<div class="section">
  <p>Section 1 content</p>
</div>

<h2>Section 2</h2>
<div class="section">
  <p>Section 2 content</p>
</div>

<h2>Section 3</h2>
<div class="section">
  <p>Section 3 content</p>
</div>

<h2>Section 4</h2>
<div class="section">
  <p>Section 4 content</p>
</div>

The normal jQuery solution for this9 would be:

$(document).ready(function(){
  $('.section').hide();
  $('h2').click(function(e){
    $(this).next().toggle();
  })
});

And then you realize that making the style of the current section deviate10 from that of the other sections would be great.

$(document).ready(function(){
  $('.section').hide();
  $('h2').click(function(e){
    $(this).next().toggle();
    $(this).next().css('background','#ccc');
    $(this).next().css('border','1px solid #999');
    $(this).next().css('padding','5px');
  })
});

A few things are wrong with this. For starters, you’ve made it hard to maintain this by controlling the look and feel in JavaScript, not CSS (more on this later). Secondly, performance: while jQuery is amazingly speedy, a lot of code is still hidden under the hood in $('.section').hide(). The last, and very painful, performance issue is the copied and pasted lines that set the CSS. Don’t ask jQuery to find the next sibling four times and do something to it. You could store the next() in a variable, but even that is not needed if you chain. If you really need to set a lot of CSS in jQuery, use a map:

$(document).ready(function(){
  $('.section').hide();
  $('h2').click(function(e){
    $(this).next().toggle().css({
      'background':'#ffc',
      'border':'1px solid #999',
      'padding':'5px'
    });
  })
});

What if you then want to allow only one of them to be open11 at any time? Inexperienced developers would do something like this:

$(document).ready(function(){
  $('.section').hide();
  $('h2').click(function(e){
    $('.section').hide();
    $(this).next().toggle().css({
      'background':'#ffc',
      'border':'1px solid #999',
      'padding':'5px'
    });
  })
});

This does the job, but you’re looping around the document and accessing the DOM a lot, which is slow. You can alleviate this by keeping the current open section in a variable:

$(document).ready(function(){
  var current = false;
  $('.section').hide();
  $('h2').click(function(e){
    if(current){
      current.hide();
    }
    current = $(this).next();
    current.toggle().css({
      'background':'#ffc',
      'border':'1px solid #999',
      'padding':'5px'
    });
  })
});

Predefine the current section as false, and set it when you click the first heading. You would then hide current only if it is true, thereby removing the need for another loop through all elements that have the class section.

But here is the interesting thing: if all you want is to show and hide sections, you don’t need any looping at all! CSS already goes through the document when it renders and applies classes. You just need to give the CSS engine something to hang on to12, such as a class for the body:

$(document).ready(function(){
  $('body').addClass('js');
  var current = null;
  $('h2').click(function(e){
    if(current){
      current.removeClass('current');
    }
    current = $(this).next().addClass('current');
  })
});

By adding the class js to the body of the document and toggling the class current for the current section, you maintain control of the look and feel in CSS:

<style type="text/css" media="screen">
  .section{
    border:1px solid #999;
    background:#ccc;
  }
  .js .section{
    display:none;
  }
  .js .current{
    display:block;
    border:1px solid #999;
    background:#ffc;
  }
</style>

The beauty of this is that the handle will be re-usable by the CSS designer and maintainer. Anything without the .js selector would be the non-scripting-enabled version of a part of the document, and anything with the .js selector is applied only when JavaScript is available. And yes, you should think about the case when it is not.

Sin #4: Depending On JavaScript And Certain Input Devices

There is quite a discussion about the need to consider non-JavaScript environments in this day and age, but here is a fact: JavaScript can be turned off, and any JavaScript could break the page for the other scripts that are included. Given the flakiness of code out there that may be running alongside yours and the instability of wireless and mobile connections, I for one want to build one thing: code that works.

So, making sure that the most basic usage of your product does not depend on JavaScript is not just nice to have but essential if you expect people to actually use the product.

Absolutely nothing is wrong with using JavaScript heavily. On the contrary, it makes the Web much smoother and saves us a lot of time if done right. But you should never promise functionality that doesn’t work. And if you rely on JavaScript, this is exactly what you’re doing. I’ve already covered the effects of bad JavaScript in detail in the AJAX13, JavaScript testing14 and security15 articles here on Smashing Magazine, but once again here are some simple steps you can take to make sure you don’t break your promise to end users:

  • Anything vital to the functionality of your product should not require JavaScript. Forms, links and server-side validation and re-direct scripts are your friends.
  • If something depends on JavaScript, build it with JavaScript and add it to the document using the DOM or the equivalent method in your library of choice.
  • If you add JavaScript functionality, make sure it works with the keyboard and mouse. Click and submit handlers are bullet-proof, whereas key and mouse events are flaky and don’t work on mobile devices.
  • By writing clever back-end code that recognizes when data is required by JavaScript rather than building APIs that render HTML, you avoid having to do double-maintenance, which is an argument that many of the “Everyone enables JavaScript” zealots bring up a lot. For proof of this, check out the presentation on building Web applications using YQL and YUI16 that I gave a few weeks ago (video in English and German).

When JavaScript Dependence Is Okay (to a Degree)

A lot of misunderstanding about JavaScript dependence stems from people making blanket statements based on the environments they work in.

If you are a Google engineer working on Gmail, you would be hard pressed to think of why you would even bother working without JavaScript. The same goes for widget developers who work on OpenSocial widgets, mobile applications, Apple widgets and Adobe Air. In other words, if your environment already depends on JavaScript, then by all means don’t bother with a fall-back.

But do not take these closed environments and edge-case applications as the standard by which we should be measuring JavaScript. JavaScript’s greatest power and greatest problem is its versatility. Saying that all websites can stand JavaScript because Gmail needs it is like saying that all cars should have a start button because they work great in Hybrids, or that hybrid cars should have massive tanks and cow catchers because they work great on Hummers. The technical feature set of a product depends on its implementation and target market. Different applications have different base functionality that needs to be satisfied in order to reach the largest audience and not block people out.

Consider the Use Cases and Maintenance

One fascinating aspect of JavaScript-dependent code is that, in many cases, people have simply not considered all the use cases (here’s a great example17). Take the following HTML:

<form action="#" id="f">
  <div>
    <label for="search">Search</label>
    <input type="text" value="kittens" id="search">
    <input type="submit" id="s" value="go">
  </div>
</form>
<div id="results"></div>

Without JavaScript, this does nothing whatsoever. There is no sensible action attribute, and the text field has no name attribute. So, even when you send the form off, the server won’t get the information that the user has entered.

Using jQuery and a JSON data source such as YQL2118, you can do a pure JavaScript search with this:

$('#s').click(function(event){
  event.preventDefault();
  $('<ul/>').appendTo('#results');
  var url =
  $.getJSON('http://query.yahooapis.com/v1/public/yql?'+
            'q=select%20abstract%2Cclickurl%2Cdispurl%2Ctitle%20'+
            'from%20search.web%20where%20query%3D%22'+
            $('#search').val() + '%22&format=json&'+
            'callback=?',
    function(data){
      $.each(data.query.results.result,
        function(i,item){
          $('<li><h3><a href="'+item.clickurl+'">'+
             item.title+' ('+item.dispurl+')</a></h3><p>'+
             (item.abstract || '') +'</p></li>').
            appendTo("#results ul");
        });
    });
});

This works… unless of course you are like me and prefer to send forms by hitting “Enter” rather than clicking the “Submit” button. Unless I tab through the whole form and focus on the “Submit” button, I get nothing.

So, that’s the first thing to fix. If you create forms, never use a click handler on the button. Instead, use the submit event of the form. This catches both the clicking “Submit” and hitting “Enter” cases. With one change, you now support all of the keyboard users out there, and the whole change is contained in the first line:

$('#f').submit(function(event){
  event.preventDefault();
  $('<ul/>').appendTo('#results');
  var url =
  $.getJSON('http://query.yahooapis.com/v1/public/yql?'+
            'q=select%20abstract%2Cclickurl%2Cdispurl%2Ctitle%20'+
            'from%20search.web%20where%20query%3D%22'+
            $('#search').val() + '%22&format=json&'+
            'callback=?',
    function(data){
      $.each(data.query.results.result,
        function(i,item){
          $('<li><h3><a href="'+item.clickurl+'">'+
             item.title+' ('+item.dispurl+')</a></h3><p>'+
             (item.abstract || '') +'</p></li>').
            appendTo("#results ul");
        });
    });
});

We’ve now covered the first case. But without JavaScript, the form still doesn’t do anything. And another problem brings us to the next sin of writing JavaScript.

Sin #5: Making Maintenance Unnecessarily Hard

One thing that keeps great code off the Web is that our work environment, deadlines and hiring practices condition developers to build code for quick release, without considering how difficult maintaining that code will be later on. I once called JavaScript the village bicycle of Web design19 (slides here20): anyone can go for a ride. Because the code is available in the open, future maintainers will be able to mess around with it and extend it any way they like.

The sad thing is that the harder your code is to maintain, the more errors will be added to it, leading it to look more like alphabet soup than organized script.

Take the above example. Those of you who haven’t worked with YQL2118 and JSON-P for cross-domain AJAX undoubtedly had a “What?” moment looking at the code. Furthermore, keeping a lot of HTML in JavaScript easy to follow is hard, and guess what is the first thing to change when a new design for the page comes along? Exactly: the HTML and CSS. So, to make it easier to maintain, I for one would shift all of the work to the back end, thus making the form work without JavaScript and keeping maintenance of all the HTML22 in the same document:

<?php
if(isset($_GET['search'])){
  $search = filter_input(INPUT_GET, 'search', FILTER_SANITIZE_ENCODED);
  $data = getdata($search);
  if($data->query->results){

    $out = '<ul>';

    foreach($data->query->results->result as $r){

      $out .= "<li>
                 <h3>
                   <a href="{$r->clickurl}">{$r->title}   
                     <span>({$r->dispurl})</span>
                   </a>
                 </h3>
                 <p>{$r->abstract}</p>
               </li>";
    }

    $out .= '</ul>';

  } else {

    $out = '<h3>Error: could not find any results</h3>';

  }
}

if($_SERVER['HTTP_X_REQUESTED_WITH']!=''){
  echo $out;
  die();
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
 "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
  <title>Ajax Search with PHP API</title>
  <link rel="stylesheet" href="styles.css" type="text/css">
</head>
<body>
  <form action="independent.php" id="f">
    <div>
      <label for="search">Search</label>
      <input type="text" value="kittens" name="search" id="search">
      <input type="submit" id="s" value="Go">
    </div>
  </form>
  <div id="results"><?php if($out!=''){echo $out;}?></div>
  <script src="jquery.js"></script>
  <script src="ajaxform.js"></script>
</body>
</html>
<?php
function getdata($search){
  $url = 'http://query.yahooapis.com/v1/public/yql?'.
         'q=select%20abstract%2Cclickurl%2Cdispurl%2Ctitle%20'.
         'from%20search.web%20where%20query%3D%22'.$search.'%22'.
         '&format=json';
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  $output = curl_exec($ch);
  curl_close($ch);
  $data = json_decode($output);
  return $data;
}
?>

Someone who doesn’t understand PHP at all should still be able to change the HTML display without breaking the code. With this in place, the JavaScript boils down to a very simple script:

$('#f').submit(function(event){
  event.preventDefault();
  $.get('independent.php?search=' + $('#search').val(),
    function(data) {
      $('#results').html(data);
    }
  );
});

The normal way to make code more maintainable is to move everything that is likely to change away from the main functional part of the script into a configuration object at the very top of the script. You can return this as an object to the outside world to allow people to set it before they initialize the main functionality.

So, one change we can make to our earlier example—albeit a small one now, but that can change quickly when more requirements come in—is to have a configuration section right up front that defines the CSS classes in use:

$(document).ready(function(){
  /* Configuration object - change classes, IDs and string here */
  var config = {
  /* CSS classes that get applied dynamically */
    javascriptenabled:'js',
    currentsection:'current'
  }

  /* functionality starts here */
  $('body').addClass(config.javascriptenabled);
  var current = null;
  $('h2').click(function(e){
    if(current){
      current.removeClass(config.currentsection);
    }
    current = $(this).next().addClass(config.currentsection);
  })
});

For more information on configuration objects and why they rock for maintenance, check out the blog post “Providing Script Configuration Inline and Programatically23“.

In summary, go over your code once more when you think you’ve finished with it and the next person is about to take it over.

Sin #6: Not Documenting Your Code

“Good code documents itself” is a terribly common and misguided belief. In my years as a developer, I’ve found that my style of coding has changed constantly. What was common knowledge and best practice in 2004 might be forgotten or even considered poor style these days.

Documenting all of the tricks and workarounds we do to make our code work in different browsers is definitely a good idea. This allows future maintainers to remove them when the targeted browser version becomes obsolete or a library function fixes the issue.

Commenting your code also allows the maintainer to trace it back to you should they need some piece of information, and it allows people who have stumbled across your script to include it in a larger solution or library (which has happened to me). Because JavaScripts tend replicate on the Web (in all of those blogs and “script collections”), it is also a way to make your name known.

Don’t go overboard with commenting, though. Obvious things don’t need to be spelled out. I have found the following situations worthy of comment:

  • Necessary hacks
    Browser hacks; content clean-up; things that should be supported server-side but are not yet.
  • Sections that are likely to change
    Timely solutions; IDs, classes and strings (as explained earlier).
  • Start of classes and reusable functions
    With name, author, version, date and license.
  • Third-party code
    Give credit where credit is due.
  • Sections with dependencies
    Some comment like, “Needs the Google API with an own key—this one will not work on your server.”

In short, comment on anything that deviates from the normal flow of coding. I tend to use /* */ instead of // because it won’t create a bug if people remove the line break by accident.

Special Case: Commenting Out Code

One special case is commenting out sections that will be necessary in future releases or that depend on functionality not currently available. This can be amazingly useful but also a security risk, depending on what you’re commenting out. For example, don’t leave in any code that points to server-side APIs that are not available yet but could at any time be half-implemented. I’ve seen this before, where administrator links with the full unprotected path were commented out in the HTML.

Still, commenting out can be very useful for debugging. One neat trick is the following:

/*

myFunction('do something');

// */

This is now commented out. But by adding a single slash in front of the first comment line, you will uncomment the whole block and make it live.

//*

myFunction('do something');

// */

This trick makes it awfully easy to toggle whole blocks.

Sin #7: Optimizing For Machines, Not People

The last sin is over-optimizing JavaScript based on the scads of information about performance that are available to us. You will find a lot of information on the Web about optimizing JavaScript for performance in the current browser environment. Notice that “current browser environment”—much information is browser- and version-specific and a necessary evil for now, but not necessarily in future. If your application is large or your website is high traffic, knowing and applying this information could make or break it. Again, though, a lot of this applies to edge cases that would have little impact on small projects and environments. This optimization does make it harder to maintain the code; some of the things we need to do to make browsers run fast on high-scale websites, such as writing out script nodes with document.write(), are downright nasty.

When faced with the choice between making code cleaner and easier to amend, extend and understand on the one hand, and shaving two milliseconds off every page load on the other, I opt for the former. A lot of JavaScript optimization can be done through scripts. And rather than teach all developers on a project the ins and outs of JavaScript performance, an expert team (or even a tool) could optimize the code before it goes live.

If you can do anything with machines to make the jobs of other machines easier, do it. The time has come for us to apply build processes as much to front-end code as we do to back-end code, instead of forcing ourselves to follow coding practices that go against the natural flow of writing code.

Further Reading

I hope you’ve gotten an idea now of how to make scripts more useful, easier to extend and safer to use. For more information, please check out the following links:

(al)

Footnotes

  1. 1 http://github.com/jquery/jquery/blob/master/src/core.js
  2. 2 http://icant.co.uk/articles/sins/sins_globals.html
  3. 3 http://icant.co.uk/articles/sins/sins_globals_fixed.html
  4. 4 https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Working_with_Closures
  5. 5 http://jquery-howto.blogspot.com/2009/01/namespace-your-javascript-function-and.html
  6. 6 http://www.wait-till-i.com/2007/07/24/show-love-to-the-module-pattern/
  7. 7 http://www.wait-till-i.com/2007/08/22/again-with-the-module-pattern-reveal-something-to-the-world/
  8. 8 http://www.jslint.com/
  9. 9 http://icant.co.uk/articles/sins/sins_sectiondemo.html
  10. 10 http://icant.co.uk/articles/sins/sins_section_with_current.html
  11. 11 http://icant.co.uk/articles/sins/sins_section_single_open.html
  12. 12 http://icant.co.uk/articles/sins/sins_section_class_handle.html
  13. 13 http://www.smashingmagazine.com/2010/02/10/some-things-you-should-know-about-ajax/
  14. 14 http://www.smashingmagazine.com/2010/01/21/find-the-right-javascript-solution-with-a-7-step-test/
  15. 15 http://www.smashingmagazine.com/2010/01/14/web-security-primer-are-you-part-of-the-problem/
  16. 16 http://www.yuiblog.com/blog/2010/02/11/video-heilmann-yql/
  17. 17 http://icant.co.uk/articles/sins/sins_jsform.html
  18. 18 http://developer.yahoo.com/yql
  19. 19 http://developer.yahoo.net/blogs/theater/archives/2008/10/fronteers_2008_christian_heilmann_on_maintain_1.html
  20. 20 http://www.slideshare.net/cheilmann/fronteers-maintainability-presentation
  21. 21 http://developer.yahoo.com/yql
  22. 22 http://icant.co.uk/articles/sins/independent.php
  23. 23 http://www.wait-till-i.com/2008/05/23/script-configuration/
  24. 24 http://carsonified.com/blog/dev/the-importance-of-maintainable-javascript/
  25. 25 http://www.wait-till-i.com/2008/02/07/five-things-to-do-to-a-script-before-handing-it-over-to-the-next-developer/
  26. 26 http://icant.co.uk/articles/pragmatic-progressive-enhancement/
  27. 27 http://www.wait-till-i.com/2007/11/28/planning-javascript-for-larger-teams-draft-handout-version/

↑ Back to topShare on Twitter

An international Developer Evangelist working for Mozilla in the lovely town of London, England.

Advertising

Note: Our rating-system has caused errors, so it's disabled at the moment. It will be back the moment the problem has been resolved. We're very sorry. Happy Holidays!

  1. 1

    I have always ignored JavaScript, and simply because I do not want to learn Yet Another Thing for the web. Because I try to ignore clientside stuff, and have control over the source server side.

    Unfortunately with todays web it’s impossible to ignore js, and with articles like this I simply learn a bit more than the basics that I have always knew. Thanks for posting.

  2. 2

    well, a very technical article… :)

    @smashingmag, can you please do something with the code quotes? it’s very unreadable… :/

  3. 3

    This is a great article, it really helps demystify javascript and all the peculiarities there are out there. I always rely on jQuery these days to do everything except for only the simplest of features.

  4. 4

    Very informative article. Thanks Christian!

    One thing, though, and this is just a stylistic preference, – shouldn’t the self-invoking function expression perhaps be wrapped in parenthesis, even when it’s being assigned to a var?

    var a = function(){…..}();
    // vs.
    var a = (function(){……})();

    The latter one makes it very clear to those who read the code that this it is a function that’ll be immediately invoked – with the former one, you’ve got to scroll all the way to the bottom to find the ..()

    Obviously this only really matters with long functions, but I think it’s pretty important to nurture this best-practice from day one, even with smaller functions.

    Again, great article!

  5. 5

    Smashing Editorial

    February 22, 2010 5:18 am

    Thomas, why is it unreadable? I believe the syntax highlighter makes it quite readable, isn’t it?

  6. 6

    Good point! This is a good practice.

  7. 7

    Nothing new!

  8. 8

    on #6:

    /**/
    “live” code
    /**/

    /** /
    commented code
    /**/

    looks nicer and is more obvious :)

  9. 9

    Nice article, but under Sin #2, there’s a small editorial mistake:

    “Fegular expressions”

    should be

    “Regular expressions”.

  10. 10

    Smashing Editorial

    February 22, 2010 5:28 am

    Thank you, it’s fixed now! Sorry for any inconvenience.

  11. 11

    New sins? Don’t worry, there will be a lot soon

  12. 12

    Which suggests the all-langauges Sin #0: Spelling mistakes in your function and variable names. If in doubt, check it!

  13. 13

    Didrik Nordström

    February 22, 2010 6:04 am

    Great post!

    I’m also a great fan of making beautiful and maintainable code. I always use jQuery but I would like to look into MooTools, primarily because it’s object oriented and is an extension of “real javascript”, the way js is meant to be written.

    However, jQuery 1.4 is pretty awesome and is a huge performance boosts, compared to earlier versions like 1.2.6.

  14. 14

    Nice trick with the slash at “commenting out” I didn’t know about it :)

  15. 15

    I’ve become well versed in JavaScript over the years that i run into more problems using frameworks especially when mixing jquery with script.alicio.us. Plus I feel i have more control when writing my own and I reuse them everywhere i want.
    Good article.

  16. 16

    the highlighter is perfect! but on my mac with safari the font is too small and very very hard to read. with firefox it looks much better…

    Safari: http://bit.ly/9PPKKd
    Firefox: http://bit.ly/cfNGBp

    hope this helps. :)

  17. 17

    Well done again Christian. And thanks James.

  18. 18

    I agree with all of these, except for the bit about relying on JavaScript being enabled. Honestly, at a certain point you become very limited without the use of JavaScript. Ajax and other features have become a staple of web development.

    I work on sites that are sometimes JavaScript heavy, and if the user had it disabled it would completely fail. But I consider that to be the same as if the user disabled CSS (which you can do). Most web designers rely on that technology as well. It doesn’t seem all to different. And I have never come across a person that has it disabled.

    What most defenders of this argument bring up is a business environment that has JavaScript disabled. Yet, workers at these environments are pretty much used to pages breaking. And businesses have eased up on this restriction.

    In fact, I still add in CSS to support IE6. Which a lot of people are now turning away from. Yet, that is by far more dangerous than not supporting JavaScript being disabled. A large amount of people and companies still use IE6 (http://bit.ly/2WdDGO – I know this is just for one domain). Although this is sad and holding back innovation, it is illogical to ignore. And, empirically the percentage of users with JavaScript disabled has dropped over the years. Last that I saw it was under 5%, and is bound to go down even more.

  19. 19

    Smashing Magazine has published misleading information in the past when it came to software development (e.g. PHP), but this is definitely NOT one of them. The author knows what he’s talking about.

    Excellent article!

  20. 20

    re: the “special case” comment blocks. I learned a slightly different, prettier, way of doing it:

    /**/
    unCommentedBlock();
    /**/

    /**
    commentedBlock();
    /**/

    Just another approach.

  21. 21

    I usually do not respond to articles but I must say that this is an excellent article
    Thanks a lot

  22. 22

    Merci beaucoup.

  23. 23

    the code is really small in safari. firefox renders larger/better.

  24. 24

    I would say that this article is very good! However, it only points to these good javascript techniques. A further read on that topic is the book “Pro JavaScript Techniques” written by John Resig – the creator of jQuery. I strongly recommend this book!

  25. 25

    Re: Styling Comment Blocks

    Commenting/uncommenting tricks are simply a waste of time. You shouldn’t have blocks of useless unused code laying around your app for you to comment/uncomment with one keystroke. If you really need the code, you should be using it – not saving it for later. (I’m talking about code you’re not sure what to do with, not code for future features.)

    No one likes to open someone else’s project and find random unused code blocks lurking about.

    Use a normal content block as a quick back-up, refactor your new code, implement it, then delete your old code. Done.

    Think of leftover commented blocks of code as a hoarder’s mentality. You don’t want to be on Oprah because of your pack rat code illness.

    That said, comment blocks are great for documenting classes and inline comments are perfect for describing complex functionality of special cases.

    Otherwise, a very good article.

  26. 26

    If you check my Ajax article here you will find that the argument that Ajax is a reason to rely on JavaScript is moot once you thought cleverly about your backend data. Of course there are reasons and environments, but relying on JavaScript to me is simply working on a web app the wrong way around.

  27. 27

    The use case is different. For debugging I use block commenting, in a live app I don’t leave them in.

  28. 28

    What about Opera guys?

  29. 29

    Really like the article, and it just so happened that I am trying to read and understand some Javascript mixed in with Coldfusion that was left behind the web developer before me. The worst part is there are no comments anywhere in the code.

  30. 30

    There is a short mention of hover in #3, but it should be a big one in #4, as hover events are completely useless on a mobile device. I’ve gone to big name sites on my phone before that have dropdown menus that I need to get to but can’t.

    As a developer, I try to think ahead when I am building a site and don’t include hover as an input method but as an added effect for those that CAN see it. Unfortunately it can be out of my control and I have to work around someone else’s desire for hover input.

  31. 31

    nice comment…

    check your website real value:

    websitereckon.com

    thanks & Regards

  32. 32

    I totally agree.

    My comment was mainly for newer programmers/web developers. Block commenting is a great tool while developing, but it’s not a place to leave your left over code scraps when you deploy.

    The ability to neatly and effectively comment and document your code is a valuable skill set. (Especially when you learn how to comment your code with descriptions and examples to align with a documentation system such as RDoc for Ruby.)

  33. 33

    Excellent article. I’ll concur with some commentators here that some sins are nothing new; however, you’ve offered practical solutions to repent and be sanctified!

    One comment mentioned the deviation from JS framework libraries as a matter of personal preference. I, too, feel the need to have control and knowledge over my own code.

    Chris, thank you for reinforcing the pitfalls of Sin #3. I have recently taken a stand to give designers or CSS developers more flexibility when working with JS-intensive applications. I’ve achieved this by searching nodes when the document is ready and binding events to them based on their class names.

    Looking forward to more insight.

  34. 34

    My browser(s) are pretty hard to read the code as well.
    OSX-Safari, OSX-chrome, OSX-Firefox

  35. 35

    Jollson Sam Varghese

    February 22, 2010 3:17 pm

    It was worth reading it. Good one!!

  36. 36

    I think there is a balance to find.

    When working on professional client work, yes, it needs to work without javascript and be accessable without flash and every thing else… and can’t seem to talk HP or Intel into giving up support for IE6…

    However, on modern sites – sites that I have control over… I don’t waste much time worrying about IE6 -OR- worrying about those with javascript disabled.

    Frankly, these people are the people who are holding back the web, and I see no reason to support them.

    And you’re correct that a well laid out backend does make it much easier to make the site work with or without ajax…

  37. 37

    Sin of the sins:
    Use onclick on H2 (#3)!
    Isn’t it better to put an A tag inside the H2 one and then handle the Anchor click event?
    That’ll make it keyboard accessible too.

    Nice article tough!

  38. 38

    Hopefully javascript can clean up and restore the reputation and performance.

  39. 39

    Excellent!

  40. 40

    Brilliant stuff. Common sense solutions delivered in human-speak.

    The appreciation of server side was a refreshing take. Front end developers are wielding new found power and bringing more functionality to the client side, this is one of the few articles I’ve seen asking to consider what should go server side.

  41. 41

    Very good article … thx for the block toggle tricks ^^

  42. 42

    Sorry, can’t help but laugh- Instead of switching to a more reliable and more widely used browser, you want every site you visit to change their code to accomodate the third-rate browser that came installed on your computer? Stick with a real browser developer like Mozilla, let Apple stick to shiny, pretty, overhyped and overpriced hardware, and we’ll all be better off.

  43. 43

    Looks great in Opera :)

  44. 44

    To a degree. You can set the tabindex of the h2 to -1 to also make it keyboard accessible. The link wouldn’t go anywhere so you are fixing an issue that is a browser problem with HTML.

  45. 45
  46. 46

    What a ridiculous bunch of arguments. The site allows comments, and that means that users can comment on its content too. It’s constructive feedback. If it is unreadable to a user, and is asked how it so, and they provide the information asked for .. It’s not a complaint, it’s feedback.

  47. 47

    Why not use an editor that does commenting for you?

    Control-K-C in Visual Studio to comment, Control-K-U to uncomment. Older VS versions were pretty iffy with Javascript, but 2008 and 2010 have it nailed.

  48. 48

    In Safari’s preferences go to Appearance and increase the fixed-width font.

  49. 49

    Javascript isnt slow, the dom is.

  50. 50

    I love that approach. Of course the user should configure his browser specially to meet the preferences of the web designer. I hate these idiot users who think that websites are supposed to be designed to meet their needs.

    The 3 Things I Like Most About Javascript

    1. It helps me get my money’s worth out of my ISP by making web pages 20 times the size they need to be.

    2. It helps my marriage by giving me more than enough web annoyances to complain to my wife about instead of complaining about her mother.

    3. And best of all, because I now keep it disabled, it vastly reduces the number of websites that work for me, helping me to reduce the excessive amount of time I had been spending on the Internet.

  51. 51
  52. 52

    Good Article. Thanks ….

  53. 53

    “I work on sites that are sometimes JavaScript heavy, and if the user had it disabled it would completely fail. But I consider that to be the same as if the user disabled CSS (which you can do). Most web designers rely on that technology as well. It doesn’t seem all to different. And I have never come across a person that has it disabled.”

    As a web developer, I would be ashamed if my web pages stopped functioning because a user did not have CSS or images. If you need those to make your page function, you are a poor designer. You’re also hatin’ on the blind or anyone with a linear browser.
    So as an argument FOR everyone-should-have-js-enabled, otherwise-to-hell-with-them, it’s a very very bad one.
    And, yes, you have now come across a person who has it disabled: me. And another: my boss’ rather new Ericsson phone, who cannot even view his web-stat accounts because some silly person used JS for the login form. It’s called Epic Fail and as Chris points out, there may be exceptions, but they are often a POOR excuse in all other cases for poorly-written sites.

  54. 54

    Doesn’t that introduce the problems of tabindex? Or is it ok because it’s negative?

    I’ve been adding anchors (with JS) to elements for when they need a click event for the reason posted above, and never considered tabindex because of its dark powers.

  55. 55

    Very interesting. Seems like I should take a look at YSQL…
    In my current Project I’m heavily using Javascript and I am worried about the maintainability of my JS Code. This Article gave me some Ideas how I could improve it.

  56. 56

    “This happens to be one reason why Internet Explorer 6 refuses to die….”
    then
    “If you run this in a browser (not Internet Explorer 6), …”
    and then no mention of why not IE6, and what you might do about that, since IE6 refuses to die…

  57. 57

    Within Sin #3, you apply a class of ‘js’ to the body element as a css hook. I’ve also noticed a certain number of other javascript apps do this, however they apply the class to the ‘html’ element, examples are Cufon and sIFR.

    My main concern with applying it to the ‘html’ element is it makes the markup invalid. However it seems to have no detrimental effect.

    Do you believe it’s better to place the class on the ‘body’ or the ‘html’ element? and why?

  58. 58

    I’d find the tutorial more useful if it was more pure to the title, and was about javascript, not json enhanced javascript. I was looking for somethign helping my javascript programming, but I’ve never used json, and that makes the code examples a little tough to grok.

    Or at least change the articles title to reflect that it is using json as it’s example base.

  59. 59

    hihhi…there’s any rss feed for magazine network…:D
    thank You…i have add ^^

  60. 60

    I agree. I like safari, because its pretty – Its faster than Firefox so fix it.

  61. 61

    You jQuery example is still very basic and long hand. Could you do something like this instead?

    All you need to do is set-up an active class in css:

    .section{
    border:1px solid #999;
    background:#fff;
    display:none;
    }
    .current{
    border:1px solid #ffc;
    background:#ccc;
    display:block;
    }

    and the add this jQuery:

    $(‘h2′).click(function(e){
    $(this).next().addClass(‘current’).end().siblings().next().removeClass(‘current’);
    });​

    The jQuery doubles up the classes to set the active one. Then uses siblings to remove it from the rest.
    (remember that end() takes the selector level back one step)

    like so… http://jsbin.com/uguja3

    : )

  62. 62

    Sorry I have to laugh that someone refers to ‘real’ web developer tools but discounts the browser running the most advanced rendering engine out there. Why do you think Chrome is running Webkit and not Gecko?

  63. 63

    Jesus. What are you doing reading this article if you consider Safari a “non-real” browser? We’re not talking about IE6 here. Your hatred towards Apple is amusing, but it’s no excuse to jump on someone’s nuts for providing legit feedback.

    Use the browser you like. Let the man use the browser he prefers.

  64. 64

    The ‘sins of our fathers’ bit reminded me of the old Douglas Crockford articles about js, written back before it was cool, http://javascript.crockford.com/.

    Also there are a couple videos of his talks up at: http://yuiblog.com/crockford/

    well worth a read/watch

  65. 65

    Great article. Regarding #3, I have always wondered if there is any performance benefit to using JQuery’s show/hide methods vs. JQuery’s addClass/removeClass methods, and using the class to toggle the display. Is the recommendation to use addClass/removeClass based on performance or simply a CSS separation best practice?

  66. 66

    We use GWT (google web toolkit) which gives us a fantastic framework, with development in java (way better) and deployment in JS. This gives us a lot of “sin protection” as java just doesn’t allow as much crap coding. Rigid type checking, range checking, variable name checking, etc, from eclipse and the java compiler prevent a lot of user errors. This of course does require JS to be enabled, but given the complexity of our application (and a closed healthcare environment), and the desire to give a desktop app feel, we don’t mind this requirement (similar to the gmail argument above)

    We do have the IE6 issues, and occasionally have fallen prey even with GWT, but this has been rare.

  67. 67

    Maybe the first thing to look up for you would be the difference between jQuery and JSON. :)

    If you want to learn about JavaScript from the ground up and learn how to avoid all these problems, check the Opera Web Standards curriculum – I’ve written a lot of articles there.

    http://dev.opera.com/articles/wsc/

    This is about the application of JavaScript nowadays and I am making the assumption that most readers here use jQuery before they write their own JavaScript solutions. And improving our use of jQuery is very important as it is a powerful tool and is constantly misused.

  68. 68

    No love for Prototype? It was making JavaScript accessible before silly jQuery was a glint in Resig’s eye.

  69. 69

    cool!!! well totaly impressed :) keep it up this informative work.. and yes u can have little gift if u think this worth for u free air ticket for u … click my name for that and yes dont forget to thank me .. i will be apriciate this web blog always..

  70. 70

    Excuse me for disagreeing, but my list of the 7 deadly JS sins reads like this:

    1. eval (it’s evil!),
    2. spamming global scope,
    3. overloading global prototypes (and forgetting hasOwnProperty),
    4. assuming existence of objects/methods (test it, baby!),
    5. functions with more than 4 arguments (use an options object instead to avoid confusion),
    6. using browser specific methods where standard methods suffice (addEventListener/attachEvent vs. node.onevent, document.all)
    and last but not least
    7. writing 2MB of JS to achieve what could have been done in 2kb.

    …and never forget: only commit those sins for a reason – and it rather be good!

  71. 71

    awesome post,,very informative article. Thanks Christian!

  72. 72

    the code is really small in safari

  73. 73

    Try command and the + sign. That shortcut automatically makes text and all bigger in Safari and most programs on a mac.
    says a mac user with Firefox, Safari and Chrome.
    And be nice all, you never know when a computer will take a dislike to someone else’s favorite browser for no apparent reason. I can hardly get my older mac online at all.

  74. 74

    I nearly cried when I saw the globals declared in example one …

    ;-)

  75. 75

    Chris, i just love your articles.

    I think the browser vendors are quite inconsiderate, why don’t they just agree on the property and drop the prefixes?

    I also don’t think we should give the user so much power – If a user disables javascript and you notify them that it’s necessary let them feel the pinch – this is a wide world full of choices and consequences

  76. 76

    I have more to add:

    – Not separating concerns – mashing HTML,CSS, JS and the API together making it hard to build upon, inefficient and just plain messy.

↑ Back to top