Christian Heilmann

Posts Tagged ‘bestpractice’

Paris Web Videos are online – check out my “Successful teams use web standards” presentation

Friday, March 7th, 2008

The lovely people at Paris Web just released all the videos of the 2007 conference on dailymotion. My talk is the only English one and deals with the topic of how following web standards helps your team to be more successful:


Successful teams use web standards!
Uploaded by parisweb

Slides:

The other videos are pretty interesting insofar as they cover accessibility and internationalization matters from a technical, social and legislative angle. My favourite was the IBM server that automatically transcribes videos by running voice recognition over the audio stream.

Most common JavaScript mistakes – Paul Boag grilled me for the Boagworld Podcast

Wednesday, February 27th, 2008

One of the biggest podcasts in the UK when it comes to web development and web design is Paul Boag’s Boagworld.com. Every week Paul talks about what’s hot and happening in the development world and mixes it with good humour and believable insights.

This week’s podcast is entitled “Hiring” but ends with an interview Paul did with me over Skype on Valentine’s. If you listen to it, you’ll hear that I did have a cold and it was a long day, and if you read the transcript, you’ll see that both Paul and me “um…” a lot.

Nevertheless Paul had some very good questions, and I hope I had interesting answers.

Five things to do to a script before handing it over to the next developer

Thursday, February 7th, 2008

Let’s face fact folks: not too many developers plan their JavaScripts. Instead we quickly write something that works, and submit it. We come up with variable and function names as we go along and in the end know that we’ll never have to see this little bit of script ever again.

The problems start when we do see our script again, or we get scripts from other developers, that were built the same way. That’s why it is good to keep a few extra steps in mind when it comes to saying “this is done, I can go on”.

Let’s say the job was to add small link to every DIV in a document with the class collapsible that would show and hide the DIV. The first thing to do would be to use a library to get around the issues of cross-browser event handling. Let’s not concentrate on that for the moment but go for oldschool onevent handlers as we’re talking about different things here. Using a module pattern we can create functionality like that with a few lines of code:

collapser = function(){
  var secs = document.getElementsByTagName('div');
  for(var i=0;i<secs.length;i++){
    if(secs[i].className.indexOf('collapsible')!==-1){
      var p = document.createElement('p');
      var a = document.createElement('a');
      a.setAttribute('href','#');
      a.onclick = function(){
        var sec = this.parentNode.nextSibling;
        if(sec.style.display === 'none'){
          sec.style.display = 'block';
          this.firstChild.nodeValue = 'collapse'
        } else {
          sec.style.display = 'none';
          this.firstChild.nodeValue = 'expand'
        }
        return false;
      };
      a.appendChild(document.createTextNode('expand'));
      p.appendChild(a);
      secs[i].style.display = 'none';
      secs[i].parentNode.insertBefore(p,secs[i]);
    }
  }
}();

This is already rather clean (I am sure you’ve seen innerHTML solutions with javascript: links) and unobtrusive, but there are some things that should not be there.

Step 1: Remove look and feel

The first thing to do is not to manipulate the style collection in JavaScript but leave the look and feel to where it belongs: the CSS. This allows for ease of skinning and changing the way of hiding the sections without having to mess around in the JavaScript. We can do this by assigning a CSS class and removing it:

collapser = function(){
  var secs = document.getElementsByTagName('div');
  for(var i=0;i<secs.length;i++){
    if(secs[i].className.indexOf('collapsible')!==-1){
      secs[i].className += ' ' + 'collapsed';
      var p = document.createElement('p');
      var a = document.createElement('a');
      a.setAttribute('href','#');
      a.onclick = function(){
        var sec = this.parentNode.nextSibling;
        if(sec.className.indexOf('collapsed')!==-1){
          sec.className = sec.className.replace(' collapsed','');
          this.firstChild.nodeValue = 'collapse'
        } else {
          sec.className += ' ' + 'collapsed';
          this.firstChild.nodeValue = 'expand'
        }
        return false;
      }
      a.appendChild(document.createTextNode('expand'));
      p.appendChild(a);
      secs[i].parentNode.insertBefore(p,secs[i]);
    }
  }
}();

Step 2: Remove obvious speed issues

There are not many issues in this script, but two things are obvious: the for loop reads out the length attribute of the secs collection on every iteration and we create the same anonymous function for each link to show and hide the section. Caching the length in another variable and creating a named function that gets re-used makes more sense:

collapser = function(){
  var secs = document.getElementsByTagName('div');
  for(var i=0,j=secs.length;i<j;i++){
    if(secs[i].className.indexOf('collapsible')!==-1){
      secs[i].className += ' ' + 'collapsed';
      var p = document.createElement('p');
      var a = document.createElement('a');
      a.setAttribute('href','#');
      a.onclick = toggle;
      a.appendChild(document.createTextNode('expand'));
      p.appendChild(a);
      secs[i].parentNode.insertBefore(p,secs[i]);
    }
  }
  function toggle(){
    var sec = this.parentNode.nextSibling;
    if(sec.className.indexOf('collapsed')!==-1){
      sec.className = sec.className.replace(' collapsed','');
      this.firstChild.nodeValue = 'collapse'
    } else {
      sec.className += ' ' + 'collapsed';
      this.firstChild.nodeValue = 'expand'
    }
    return false;
  }
}();

Step 3: Removing every label and name from the functional code

This makes a lot of sense in terms of maintenance. Of course it is easy to do a quick search + replace when the label names or class names have to change, but it is not really necessary. By moving everything human readable into an own config object you won’t have to hunt through the code and suffer search + replace errors, but instead keep all the changing bits and bobs in one place:

collapser = function(){
  var config = {
    indicatorClass : 'collapsible',
    collapsedClass : 'collapsed',
    collapseLabel : 'collapse',
    expandLabel : 'expand'
  }
  var secs = document.getElementsByTagName('div');
  for(var i=0,j=secs.length;i<j;i++){
    if(secs[i].className.indexOf(config.indicatorClass)!==-1){
      secs[i].className += ' ' + config.collapsedClass;
      var p = document.createElement('p');
      var a = document.createElement('a');
      a.setAttribute('href','#');
      a.onclick = toggle;
      a.appendChild(document.createTextNode(config.expandLabel));
      p.appendChild(a);
      secs[i].parentNode.insertBefore(p,secs[i]);
    }
  }
  function toggle(){
    var sec = this.parentNode.nextSibling;
    if(sec.className.indexOf(config.collapsedClass)!==-1){
      sec.className = sec.className.replace(' ' + config.collapsedClass,'');
      this.firstChild.nodeValue = config.collapseLabel
    } else {
      sec.className += ' ' + config.collapsedClass;
      this.firstChild.nodeValue = config.expandLabel
    }
    return false;
  }
}();

Step 4: Use human-readable variable and method names

This is probably the most useful step when it comes to increasing the maintainability of your code. Sure, during development sec made a lot of sense, but doesn’t section make it easier to grasp what is going on? What about a, and especially when it needs to be changed to a button later on? Will the maintainer rename it to button?

collapser = function(){
  var config = {
    indicatorClass : 'collapsible',
    collapsedClass : 'collapsed',
    collapseLabel : 'collapse',
    expandLabel : 'expand'
  }
  var sections = document.getElementsByTagName('div');
  for(var i=0,j=sections.length;i<j;i++){
    if(sections[i].className.indexOf(config.indicatorClass) !== -1){
      sections[i].className += ' ' + config.collapsedClass;
      var paragraph = document.createElement('p');
      var trigger = document.createElement('a');
      trigger.setAttribute('href','#');
      trigger.onclick = toggleSection;
      trigger.appendChild(document.createTextNode(config.expandLabel));
      paragraph.appendChild(trigger);
      sections[i].parentNode.insertBefore(paragraph,sections[i]);
    }
  }
  function toggleSection(){
    var section = this.parentNode.nextSibling;
    if(section.className.indexOf(config.collapsedClass) !== -1){
      section.className = section.className.replace(' ' + config.collapsedClass,'');
      this.firstChild.nodeValue = config.collapseLabel
    } else {
      section.className += ' ' + config.collapsedClass;
      this.firstChild.nodeValue = config.expandLabel
    }
    return false;
  }
}();

Step 5: Comment, sign and possibly eliminate the last remaining clash with other scripts

The last step is to add comments where they are really needed, give your name and date (so people can ask questions and know when this was done), and to be really safe we can even get rid of the name of the script and keep it an anonymous pattern.

//  Collapse and expand section of the page with a certain class
//  written by Christian Heilmann, 07/01/08
(function(){
 
  // Configuration, change CSS class names and labels here
  var config = {
    indicatorClass : 'collapsible',
    collapsedClass : 'collapsed',
    collapseLabel : 'collapse',
    expandLabel : 'expand'
  }
 
  var sections = document.getElementsByTagName('div');
  for(var i=0,j=sections.length;i<j;i++){
    if(sections[i].className.indexOf(config.indicatorClass)!==-1){
      sections[i].className += ' ' + config.collapsedClass;
      var paragraph = document.createElement('p');
      var triggerLink = document.createElement('a');
      triggerLink.setAttribute('href','#');
      triggerLink.onclick = toggleSection;
      triggerLink.appendChild(document.createTextNode(config.expandLabel));
      paragraph.appendChild(triggerLink);
      sections[i].parentNode.insertBefore(paragraph,sections[i]);
    }
  }
  function toggleSection(){
    var section = this.parentNode.nextSibling;
    if(section.className.indexOf(config.collapsedClass)!==-1){
      section.className = section.className.replace(' ' + config.collapsedClass,'');
      this.firstChild.nodeValue = config.collapseLabel
    } else {
      section.className += ' ' + config.collapsedClass;
      this.firstChild.nodeValue = config.expandLabel
    }
    return false;
  }
})();

All very obvious things, and I am sure we’ve all done them before, but let’s be honest: how often do we forget them and how often do you have to alter code where it’d have been nice if someone had taken these steps?

Planning JavaScript for Larger Teams – Draft & Handout version

Wednesday, November 28th, 2007

I just found my original draft of my @mediaAjax presentation. I discarded it cause it was way too long for an hour talk and I didn’t know what do ditch:

Hope it is of use somehow.

JavaScript shortcut notations that shouldn’t be black magic to the “average developer”

Tuesday, November 27th, 2007

When publishing articles about JavaScript development you run into many rules set by publishers and editors. This is a great thing as it keeps us authors on our toes and makes us understand more about how much work successful publishing really is. However, it can also be terribly annoying, especially when older idioms just stick. One of them is that “the average developer” does not quite understand JavaScript shortcut notations.

Now, if you are “the average developer”, please give me 5 minutes of your time to get through the following, you’ll understand a lot more code out there and also spend a lot less time writing your own scripts.

Array shortcut notation – It is hip to be square

The first bugbear I have are arrays. These are terribly useful and omnipresent in JavaScript and still I find people who write convoluted things like:

var links = new Array();
links[0] = 'http://cuteoverload.com';
links[1] = 'http://icanhascheezburger.com';
links[2] = 'http://pencilsatdawn.wordpress.com';
links[3] = 'http://apelad.blogspot.com';
// ... ad nauseam ...

This is convoluted (you have to repeat the array name for every item) and also tricky to maintain. When you change the order (granted the order is of importance) you need to change the number, too. It is not necessary, as the numbering is done automatically for you. All you need to do is use the square brackets:

var links = [
  'http://cuteoverload.com',
  'http://icanhascheezburger.com',
  'http://pencilsatdawn.wordpress.com',
  'http://apelad.blogspot.com' // <-- last one, NO COMMA!
];

This makes it more obvious from a visual point of view, too. The indentation makes it easy to spy where the array begins and where it ends. Much less line noise to take in.

Notice that you need to separate each item with a comma, but make sure you don’t have a trailing comma at the last item. You can even nest arrays that way:

var links = [
  'http://cuteoverload.com',
  'http://icanhascheezburger.com',
  [100,200,20,'foo'],
  'http://pencilsatdawn.wordpress.com',
  'http://apelad.blogspot.com' // <-- last one, NO COMMA!
];

On that note, here’s another trick: to add a new item to arrays, you can either use the push() method or use the length property:

links.push('http://dailypuppy.com');
links[links.length] = 'http://bigeyedeer.wordpress.com';

Associative Arrays – OMG! Ponies!

One myth that keeps sticking is that JavaScript has a magical thing called associative arrays. These allow you to not just number the items but give them names instead! You find examples like:

var links = new Array();
links['Cute Overload'] = 'http://cuteoverload.com';
links['I can has cheeseburger'] = 'http://icanhascheezburger.com';
links['Pencils at dawn'] = 'http://pencilsatdawn.wordpress.com';
links['Hobotopia'] = 'http://apelad.blogspot.com';

This is treachery to the highest degree. It is a confusing JavaScript trait, as what you created here is not really an array, but an object. Want proof? – Do an alert(typeof links) or try alert(links[1]).

Object shortcut (literal) notation – getting curly.

So called associative arrays are actually objects, which means you will also find other scripts that took the earlier example serious and use the following instead:

var links = new Object();
links['Cute Overload'] = 'http://cuteoverload.com';
links['I can has cheeseburger'] = 'http://icanhascheezburger.com';
links['Pencils at dawn'] = 'http://pencilsatdawn.wordpress.com';
links['Hobotopia'] = 'http://apelad.blogspot.com';

Again we have far too much repetition. For objects, you have the Object Literal Notation as a shortcut, which basically is using curly braces:

var links = {
  'Cute Overload' : 'http://cuteoverload.com',
  'I can has cheeseburger' : 'http://icanhascheezburger.com',
  'Pencils at dawn' : 'http://pencilsatdawn.wordpress.com',
  'Hobotopia' : 'http://apelad.blogspot.com' // <-- again, no comma!
}

The semicolons get replaced by commatacommas (Happy, Stuart?) (except for the last one which you need to omit) and the equal signs become colons. If the properties (the things to the left of the colon) don’t have any spaces in them, you can even get rid of the quotes:

var chris = {
  hair : 'red',
  age : 32,
  city : 'London'
}

You can access the properties with object.property when they don’t have any spaces or with object[property] when they have spaces. For example:

var links = {
  'Cute Overload' : 'http://cuteoverload.com',
  'I can has cheeseburger' : 'http://icanhascheezburger.com',
  'Pencils at dawn' : 'http://pencilsatdawn.wordpress.com',
  'Hobotopia' : 'http://apelad.blogspot.com' // <-- again, no comma!
};
alert(links['I can has cheeseburger']);
alert(links.Hobotopia);

Simple if-else statements – ask and define with the ternary notation

You can shorten simple if statements dramatically. With simple I mean an if statement that assigns one of two values like this:

  var YUIguy;
  if(city === 'London'){
    YUIguy = 'Chris';
  } else {
    YUIguy = 'Eric';
  };

Again, a lot of repetition there. The ternary notation works around that problem:

var YUIguy = city === 'London' ? 'Chris' : 'Eric';

A lot of equal signs there. Maybe some parenthesis will make it clearer:

var YUIguy = (city === 'London') ? 'Chris' : 'Eric';

What’s going on here? You define the variable YUIguy and assign it a value. Then you have a statement, in this case the comparison of the variable city and if it equals to the string London (=== tests for both the value and the type, much safer than ==).

Then you ask the question if that is true or not by using the question mark. The option on the left of the colon is the answer to the question when the condition was met and the option on the right of the colon is the answer when the condition was not met. Any condition that could be true or false can go inside the parenthesis.

var direction = (x < max) ? 'left' : 'right';

Offering a fallback option with the default operator

The last thing I wanted to quickly talk about is the double pipe (||) default operator. This one is terribly useful when you want to make sure that something is set with a default value. The following construct is something that should not show up any longer:

  var section = document.getElementById('special');
  if(!section){
    section = document.getElementById('main');
  }

Again, useless repetition, as the same can be written as:

  var section = document.getElementById('special') || document.getElementById('main');

If the first is not defined, the second gets assigned as a value to section.

Thanks for your time!

I hope this helped “the average developer” to understand how you can make your JS a lot shorter without really cutting down on readability. If you want more detailed information about all these goodies, check out Douglas Crockford’s articles on the matter: