By Garrett Smith, with contributions from Asen Bozhilov, RobG, John G Harris, David Mark, and Dmitry Soshnikov.
This document points to the causes of errors and problems related to javascript for web applications and provides explanation of what to do instead.
When followed, these guidelines can improve the code stability and maintainability of a Rich Internet Application deployed across a wide range of web browsers.
For code reviews, this document and the Code Review Guidelines should be read by the code author and code reviewer.
Use valid html validator.w3.org.
Code that uses malformed, nonconforming HTML is expecting nonstandard behavior. When a web browser encounters a problem in HTML, it must perform error correction. Since there are no error correction mechanisms defined in HTML 4, browsers handle the problem in proprietary ways.
When the browser parses a document, it creates a DOM representation of it. If the browser performed error correction when parsing, the outcome is a different DOM. For an example with a detailed explanation, see: Tag Soup: How UAs handle <x> <y> </x> </y> and also Avoiding Structural Differences in the Browser DOMs.
The W3C validator results either with green PASS
or with one or more errors
at which point it is clear that the code is not completed to
standard.
Use standards mode, with HTML DOCTYPE
first (neither comments nor xml declaration preceeding it).
Reduce the markup to simplest semantic markup necessary.
Simpler code is easier to read.
More markup means more bytes transferred over the wire, more parsing for the browser, and a larger DOM for scripts to traverse.
Set the HTTP content-type text/html.
Set charset in HTTP or, if using a META
tag, as
early as possible
in the HEAD
.
Escape ETAGO delimiter with backwards solidus (backslash).
See also : The reverse solidus (backslash, \
) in Web authoring.
A reverse solidus (backslash) should appear before the solidus symbol in the sequence "</", resulting in "<\/", and not "<" + "/". ETAGO in SCRIPT and STYLE.
Do not use javascript:
pseudo protocol.
FAQ: I have <a href="javascript:somefunction()"> what ... ?
The W3C validator does not validate conformance. Conformance errors can cause problems and must be recognized and corrected. See also: Appendix B: Performance, Implementation, and Design Notes: Invalid Documents, which states This specification does not define how conforming user agents handle general error conditions...
When the code does not do what is wanted of it, it is important to understand why. The HTML validator can help because it can report HTML errors (and much more quickly than a tired human eye could).
Poring over a long list of validation errors to find the validation error(s) that could be related to the code not doing what is wanted of it is error-prone, time-consuming guesswork. When an HTML error is found, the next task is finding when that error crept in, why it is there, what other code is relying on the problematic area, who wrote that other code, where is he now, so a possible fix can be discussed, etc. Using invalid HTML is a waste of time. Instead, by always using valid HTML, clear expectations can be made of what the code should be doing.
Running the W3C validator is a little extra effort that saves a lot of time. See also: W3C: Why Validate?
Use valid css. (Excepting vendor extensions and css 3 features). http://jigsaw.w3.org/css-validator/
Class and id selectors should have semantic meaning. See also:
Use class with semantics in mind.
Selectors such as .redButton
, #ItalicStatement
are meaningless.
Instead, use class and id that represent an object or state.
.errorButton, #warningMessage, .active-panel
This makes the code meaningful even when the styles change.
Declare variables in the narrowest possible scope, never global.
Do not assign to undeclared identifiers (do not forget var
).
Give each identifier a meaningful name.
goog.isDef = function(val) { return val !== undefined; };
substr
,
escape
, getYear
, parseInt
with no radix). Instead, use
substring
, encodeURIComponent
, getFullYear
,
and parseInt(s, 10)
with a radix, respectively.
A piece of code that modifies any object it does not own is not clear to the client of the API where those changes are coming from.
Modifying built-ins has been known to cause forwards-compatibility problems and conflict with other code. Modifying host objects can result in javascript errors.
Instead, use another approach. For example, define your own object, possibly hidden in a closure.
Modifying host objects or host objects' constructors' prototypes with new properties is unspecified. The practice has been known to be the source of cross-browser problems.
Where DOM host objects are implemented with prototypes, the prototype chain is implementation-dependent (not specified). The interface-based API design of the W3C DOM does not forbid a sub-interface from providing a more specific implementation that could shadow the a user-defined method of the same name further up in the prototype chain.
The W3C DOM does not prevent implementations from creating their own interfaces. Quite often implementations do create interfaces that are interwoven through the interface hierarchy. Such interfaces may or may not be accessible to code.
String.prototype.concat(a, b, c)
or
htmlArray.push(a, b, c);
For Styles, replace a loop that applies styles to descendants by adding a class token to the nearest common ancestor (example).
.special-state .special-descendant-class
.
"special-descendant-class"
.
"special-descendant-class"
.
For events, use event delegation. That is, replace a loop that adds a callback to each element in a collection with a callback on a common ancestor.
Do not use ==
where strict equality is required.
Where strict equality is required, the strict equality operator ===
must be used.
The strict equality operator should always be used to compare objects.
Do not use Boolean conversion of value that may be acceptably falsish,
e.g. if(e.pageX)
.
Instead check the property using typeof
. For example:
if(typeof e.pageX == "number")
.
Do not use useless statements (e.g. typeof it == "array"
). In addition to
contributing to code size and runtime performance, useless statements make the
code harder to understand.
The context of a Regular Expression is as important as what it can match. A complex regular expression is harder to understand than a simple one. Simple regular expressions are preferred. It is sometimes acceptable for the expression to match the wrong thing, just so long as the context in which it is used precludes or handles that.
// Wrong: Matches 137, 138... ARROW_KEY_EXP : /37|38|39|40/; // Right: matches only arrow keys. ARROW_KEY_EXP : /^(?:37|38|39|40)$/; // Simple: Can match the wrong thing, but that can be handled. var iso8601Exp = /^\s*([\d]{4})-(\d\d)-(\d\d)\s*$/;
Trying to match leap years would be excessively complex. Instead, the date validation can be addressed where the expression is used.
As with functions, test not only that it succeeds correctly, but also that it fails correctly.
Avoid mistakes caused by ASI.
In ECMAScript, line terminators affect how a program is interpreted. This is a design mistake of the language of which the details are complicated. The following guidelines can help avoid ASI problems with both restricted productions and productions that are not restricted.
Restricted productions include only the postfix operators and the control
statements return
, throw
, continue
, and
break
. ASI affects a restricted productions
when a line terminator is introduced. The following guidelines help avoid that mistake:
++
or --
operator must appear
on the same line as its operand.
return
or throw
statement must
start on the same line as the return
or throw
token.
break
or continue
statement
must be on the same line as the break
or continue
token.
Don't rely on ASI. Use semicolons explicitly.
A program that uses semicolons explicitly is less susceptible to different interpretation caused by changes in line terminators and expressions.
For more details, please see: Automatic Semicolon Insertion : Unrestricted Productions
While it is important use semicolons explicitly, where required, there are cases where a semicolon should not be added.
The Statements that do not end in semicolon all end in close curly braces instead. They are :
Block: { ... } SwitchStatement: switch ( ... ) { ... } TryStatement: try ... catch ( ... ) { ... } try ... finally { ... }
Something that isn't a statement but looks like one:
Function Declaration: function x{ ... }
A semicolon that immediately follows a FunctionDeclaration is interpreted as an EmptyStatement. Useless statements should be avoided.
delete
operator with host object This will cause errors in IE, and others:
delete window.location; //Security error" code: "1000"
unselectable
is safe).
See also: msdn expando.
[[Get]]
access are often
ActiveX
objects. These include, but are not limited to:
parentNode
is not an element (node.offsetParent
).
XMLHttpRequest
methods (open
, send
, etc).
elem.filters.alpha
, elem.style.filters.alpha
, etc.
document.styleSheets[99999]
- Error from [[Get]]
for a
nonexistent numeric property of a styleSheets
collection.
link.href
for nntp:
links in IE.
NodeList
in Safari 2 - do not attempt access a
nonexistent property
(e.g. document.childNodes.slice
).
[[ToString]]