Code style guide

Formatting

Indentation

Each indentation level is made up of one tab. Do not use spaces. This allows each person to customize the size of their tab for their visual needs. Read why we should default to Tabs instead of Spaces for an accessible first environment for more discussion.


if (true) {
  doSomething();
}

Statement termination

End all statements with a semi-colon in JS and CSS.


return 'this is a random string';

Line length

Code and documentation line length is no more than 100 columns.

The exception to this is if the code is an HTML injection into the DOM. This code remains condensed in one strong as long as necessary.


el.append('<div class="container"><p>Do you truly intend to
perform this action?</p><button class="btn primary"
type="button">I do</button></div>');

If a line reaches the maximum length, break it on an operator or a chain (a comma or period) for easiest reading. The next line is indented one additional level. Align chained commands.


$.get(url)
.fail(function() {
return false;
})
.done(function() {
return true;
});

For assigning variable values, align the overflow with the column of the first value.


var results = thisIsAVariable + thisIsAnotherVariable +
              probablyOneMoreVariable;

Blank lines

Add one blank line in between methods, as well as within methods before flow control statements, and style statements.


//comment
if (statement) {
  var el = 0;

  if (var === 0) {
    return true;
  }
}

Quotations

Use single quotes for all variables and related code; use double quotes when writing HTML inside the single quote variables.


var html = '<h1 class=”h1”>Welcome</h1>';

Naming

For JS, variable and function names must be descriptive but kept as short as possible. Use camel-casing for multi-word names. Variable names start with a noun and function names start with a verb. The variable name should indicate the type of variable, e.g. count, length, and size are likely to be integers.

Some verb name starters for functions:

can-
function returns a boolean
Example: canHydrate
has-
function returns a boolean
Example: hasContent
is-
function returns a boolean
Example: isChecked
get-
function returns a nonboolean
Example: getItemList
set-
function is used to save a value
Example: setItemList

Some abbreviated variable names are acceptable such as those used through multiple methods or style statements.


var t = $(this);
var pid = person id;
var rid = request id;
var lid = list id;
var el = event listener;
var d = data object;

Constants

Variables intended to be constant are styled in uppercase with underscores to separate words in the name. Any variable styled as a constant should not change.


if (count < MAX_COUNT) {
  doSomething();
}

Constructors

When creating objects, use Pascal camel case and a noun to more clearly differentiate it from functions.


function Person(name) {
  this.name = name;
}
Person.prototype.sayName = function() {
  alert(this.name);
}
var me = new Person('George');

Decimals

Do not leave a decimal point hanging (e.g. 1. or .1 should be 1.0 or 0.1).

Null vs. undefined


var person = null; //null
var person; //undefined

Arrays

Do not explicitly state the constructor when creating an array. Remove spaces between values unless it cannot fit on one line. In that case, the opening bracket remains on the initial declaration line and each value has its own line.


var colors = ['red','yellow','blue'];
var years = [
  '1971',
  '1983',
  '1988',
  '1990',
  '1995',
  '1999',
  '2008'
];

Comments and documentation

Use JSDoc for properly documenting javascript functions. Also include single line comment lines to clarify what the code is doing, especially if it could be easily misunderstood. Use single line or multi-line comments to comment out code chunks.


/** @description Alerts the person that an error was found
    @fires alertmessage
*/
function alertUser(message) {
  //the user id is found in the DOM
  var id = getId();
  //var otherId = soemthing;
  /* if(id === otherId) {
      alertmessage();
  }
  */
}

Debugging

When working with code in development mode, prefer using console.log() over alert() to display data in the browser. For more advanced logging, use console.info() (informational), console.warn() (warning), and console.error() (error that is also counted towards all page errors).

Always comment out or remove all alert and console calls in the code before releasing code to production.

Statements and expressions

Always use brackets and semi colons. Put a space between the function parameters (the parenthetical variables) and the bracket. Put a space between an expression and the conditions, but not within the condition parentheses or within the function parameters.


function hello() {
  console.log('Hello.');
}
if (state) {
  hello();
}

Switch

Switch statements follow similar bracket styling. Each case is indented, and the code listed on the next line. Cases are not separated by extra breaklines.

The Default case is required. Add break statement to the end of every case unless falling through (the ability to "or" cases together. Falling through may only be done if the previous cases do not contain additional code.


switch(action) {
  case 'login':
    login();
    break;
  case 'timeout':
  case 'logout':
    logout();
    break;
  default:
    console.log('No action');
}

With

Do not use With coding.

For

Use to iterate through an array. use for-in to iterate through objects.

Variables, operators, and functions

Declaring variables and functions

Declare all variables at the beginning of the function in a single variable initiation call. Combine similar attributes together and order accordingly if a variable is dependent on another.

Functions must be placed above any calls to that function. When nesting a function within a function, place the function right below the variable declarations and before the logic begins. Nested functions cannot be declared inside block statements (e.g. if/else).


function getList() {
  var t = $(this),
    list = t.closest('ul'),
    pid = getId(),
    x;
  function checkLogin(pid,t) {
    return login(pid);
  }
  if (checkLogin) {
    x = list;
  }
}

Additionally, a variable may immediate invoke a function to assign a value. Include parentheses around the function.


var pid = function() {
  return getId();
}

Equal

Use == for comparing values with loose equality (same value but could be different types, e.g. 5 == "5" will return true), and use === for strict equality (value and type must be the same, e.g. 5 === "5" will return false). Prefer === when performing comparisons.

Unary operators

Prefer to use prefix unary operators over postfix (++a over a++) to avoid potential issues in code execution order (prefix will increment before being executed when nested inside an executable statement such as alert(++a);).

Ternary operators

Where possible, write code with ternary operators to reduce code. Syntax is condition ? true : false.


var highscore = (player1 > player2) ? player1 : player2;
//equivalent to
var highscore;
if (player1 > player2) {
  highscore = player1;
else {
  highscore = player2;
}

Loosely couple UI layers

Use as much loose coupling between front end layers (HTML, CSS, and JS) as possible so they do not rely heavily on each other (reduces errors when changing code).

Do not mix CSS and JS; JS only exists in JS files and CSS only in CSS files (e.g. do not use CSS expressions and apply classes and not styles in JS).

Do not use JS HTML embedded event handlers, such as onClick.

When generating HTML from JS, use Handlebars to create the templates to preserve loose coupling.

Avoid globals

Write as much of the data local to the function rather than using globals.

Note: A variable declared locally but missing var becomes global (e.g. instantiated as title = "Test" rather than var title = "Test"). Ensure proper syntax is followed.

When creating HTML templates for javascript, use Handlebars rather than inserting HTML into the JS file.

If global variables cannot be avoided, use the one-global approach with namespaces.

Event handling

Decouple events from application logic; a function should not be defined within an event handler, only called.

When calling a function for an event, only pass the data in the event logic needed for that function (e.g. not event but event.clientX as a parameter) and not the entire event object.

The event handler function should also handle the event default actions such as preventDefault() and stopPropagation().


var application() = {
  clickEvent: function(e) {
    e.preventDefault();
    e.stopPropagation();

    this.showTip(e.clientX,e.clientY);
  },
  showTip: function(x,y) {
    var tip = $('#tip');
    tip.addClass('hasValue');
  }
};
addListener(el,'click',function(e) {
  application.clickEvent(e);
});

Avoid null comparisons

Use typeof to detect the correct primitive type (string, number, Boolean, null, and undefined). Use null only if null is an expected type (such as determining the existence of an element).


if (tyopeof name === 'string') {}
if (tyopeof count === 'number') {}
if (tyopeof found === 'boolean' && found) {}
if (tyopeof app === 'undefined') {}

For reference values, or objects, use instanceof to determine type, which also includes the prototype chain (e.g. Date type is a child of Object, so test would pass for both). Note: instanceof detection does not work if data is being passed to different frames.


if (value instanceof Date) {/*date object*/}
if (value instanceof RegExp) {/*regular expression object*/}
if (value instanceof Error) {/*error object*/}

To detect functions in browsers not IE8 or older, use typeof.

Arrays may be identified with the isArray() function, although adding older checks (for browsers using ECMAScript 4 or older) may be valuable.


function isArray(value) {
  if (typeof Array.isArray === 'function') {
    return Array.isArray(value);
  }
  else {
    return Object.prototype.toString.call(value) === '[object Array]';
  }
}

Checking the existence of object properties, use "in" testing. To check a specific instance, hasOwnProperty() may work if IE8 and earlier is not supported.


var object = {
  count: 0,
  related: null
}
if ('count' in object) {}

Configuration data

Configuration data consists of any hardcoded values such as URLs, strings returned to the UI (e.g. error messages), repeated unique values (e.g. a class name), settings, and any changeable values.

Create a separate config file to manage all the values and call the references in the code:

config.js


var config = {
  'MSG_INVALID_VALUE': 'Invalid value';
  'URL_INVALID': '/errors/invalid.html';
};

application.js


var validate(value) {
  if (!value) {
    alert(config.MSG_INVALID_VALUE);
    location.href = config.URL_INVALID;
  }
}

Error throwing

Include custom error messages to more easily find erroneous code. Best used in utility functions often used in multiple places.


function getDivs(el) {
  if (el && el.getElementsByTagName) {
    return el.getElementsByTagName('div');
  }
  else {
    throw new Error ('getDivs(): Argument must be a DOM element.');
  }
}

Ordering CSS properties

Organize CSS properties by purpose.

  1. Box model properties
  2. Layout properties
  3. Background properties
  4. Typographic properties
  5. Colors and borders
  6. Effects including animation