Javascript Best Practices
Introduction
This document is a list of best practices and preferred ways of developing javascript code, based on opinions and experience from many developers in the javascript community. Since this is a list of recommendations rather than a list of absolute rules, experienced developers may have slightly differing opinions from those expressed below.
Always Use 'var'
Variables in javascript either have global scope or function scope, and using the 'var' keyword is vital to keeping them straight. When declaring a variable for use either as a global variable or as a function-level variable, always prefix the declaration with the 'var' keyword. The example below highlights the potential problem caused by not doing so.
var i=0; // This is good - creates a global variable function test() { for (i=0; i<10; i++) { alert("Hello World!"); } } test(); alert(i); // The global variable i is now 10!
Since the variable i inside the function was not declared as a function-level variable by using the 'var' keyword, it references the global variable in this example. It is a good idea to always declare global variables using 'var', but it is vital to declare function-scoped variables using 'var'. The two approaches below are functionally identical.
function test() { var i=0; for (i=0; i<10; i++) { alert("Hello World!"); } }
function test() { for (var i=0; i<10; i++) { alert("Hello World!"); } }
Feature-Detect Rather Than Browser-Detect
Some code is written to detect browser versions and to take different action based on the user agent being used. This, in general, is a very bad practice. Any code which even looks at the global "navigator" object is suspect.
The better approach is to use feature detection. That is, before using any advanced feature that an older browser may not support, check to see if the function or property exists first, then use it. This is better than detecting the browser version specifically, and assuming that you know its capabilities. An in-depth article about this topic can be found athttp://www.jibbering.com/faq/faq_notes/not_browser_detect.html.
if (document.getElementById) { var element = document.getElementById('MyId'); } else { alert('Your browser lacks the capabilities required to run this script!'); }
Use Square Bracket Notation
When accessing object properties that are determined at run-time or which contain characters not compatible with dot notation, use square bracket notation. If you are not an experienced javascript programmer, it's not a bad practice to use square bracket notation all the time.
Objects properties in javascript can be accessed primarily in two ways: Dot notation and Square bracket notation.
MyObject.property
MyObject["property"]
With dot notation, the property name is hard-coded and cannot be changed at run-time. With bracket notation, the property name is a string which is evaluated to resolve the property name. The string can be hard-coded, or a variable, or even a function call which returns a string property name.
If a property name is being generated at run-time, the bracket notation is required. For example, if you have properties "value1", "value2", and "value3", and want to access the property using a variable i=2:
MyObject["value"+i]
MyObject.value+i
Also, in some server-side environments (PHP, Struts, etc) form field names are appended with [] to denote that the form field should be treated as an array on the server-side. However, referencing a field name containing [] using dot notation will not work because [] is the syntax for reference a javascript array. So, square bracket notation is required.
formref.elements["name[]"]
formref.elements.name[]
The recommendation for using square bracket notation is to always use it when it is required (obviously). Using it when not strictly required is a matter of personal preference and convention. One good rule of thumb is to use dot notation to access standard properties of objects, and square bracket notation to access properties which are defined as objects in the page. So, while document["getElementById"]() is perfectly vaid using square bracket notation, document.getElementById() is the preferred syntax because getElementById is a standard property of the document object as defined in the DOM specifications. Mixing the use of dot and square bracket notation makes it clear which properties are standard and which are names defined by the content:
document.forms["myformname"].elements["myinput"].value
Here, the forms property is a standard property of document, while the form name myformname is defined by the page content. Likewise, the elements property and value property are both defined by the specs, but then myinput name is defined in the page. This syntax is very clear and easy to understand and is a recommended convention to follow, but not a strict rule.
Avoid 'eval'
The eval() function in javascript is a way to run arbitrary code at run-time. In almost all cases, eval should never be used. If it exists in your page, there is almost always a more correct way to accomplish what you are doing. For example, eval is often used by programmers who do not know about using Square Bracket Notation.
The rule is, "Eval is evil." Don't use it unless you are an experienced developer and know that your case is an exception.
Reference Forms and Form Elements Correctly
All forms in an HTML form should have a name attribute. For XHTML documents, the name attribute is not required and instead the form tag should have an id attribute and should be referenced using document.getElementById(). Referencing forms using indexes, such as document.forms[0] is a bad practice in almost all cases. Some browsers make the form available as a property of the document itself using its name. This is not reliable and shouldn't be used.
The example below uses square bracket notation and correct object references to show the most fool-proof way of referencing a form input.
document.forms["formname"].elements["inputname"]
document.formname.inputname
If you will be referencing multiple form elements within a function, it's best to make a reference to the form object first and store it in a variable. This avoids multiple lookups to resolve the form object reference.
var formElements = document.forms["mainForm"].elements; formElements["input1"].value="a"; formElements["input2"].value="b";
When validating an input field using onChange or similar event handlers, it is always a good idea to pass a reference to the input element itself into the function. Every input element within a form has a reference to the form object that it is contained in.
<input type="text" name="address" onChange="validate(this)"> function validate(input_obj) { // Get a reference to the form which contains this element var theform = input_obj.form; // Now you can check other inputs in the same form without // hard-coding a reference to the form itself if (theform.elements["city"].value=="") { alert("Error"); } }
By passing a reference to the form element and accessing its form property, you can write a function which does not contain a hard reference to any specific form name on the page. This is a good practice because the function becomes more reusable.
Avoid 'with'
The 'with' statement in javascript inserts an object at the front scope chain, so any property/variable references will first try to be resolved against the object. This is often used as a shortcut to avoid multiple long references.
with (document.forms["mainForm"].elements) { input1.value = "junk"; input2.value = "junk"; }
The problem is that the programmer has no way to verify that input1 or input2 are actually being resolved as properties of the form elements array. It is checked first for properties with these names, but if they aren't found then it continues to search up the scope chain. Eventually, it reaches the global object where it tries to treat "input1" and "input2" as global variables and tries to set their "value" properties, which result in an error.
Instead, create a reference to the reused object and use it to resolve references.
var elements = document.forms["mainForm"].elements; elements.input1.value = "junk"; elements.input2.value = "junk";
Use onclick In Anchors Instead Of javascript: Pseudo-Protocol
When you want to trigger javascript code from an anchor <A> tag, the onclick handler should be used rather than thejavascript: pseudo-protocol. The javascript code that runs within the onclick handler must return true or false (or an expression than evalues to true or false) back to the tag itself - if it returns true, then the HREF of the anchor will be followed like a normal link. If it returns false, then the HREF will be ignored. This is why "return false;" is often included at the end of the code within an onclick handler.
<a href="javascript_required.html" onclick="doSomething(); return false;">go</a>
In this case, the "doSomething()" function (defined by the user somewhere in the page) will be called when the link is clicked, and then false will be returned. The href will never be followed for javascript-enabled browsers. However, if the browser does not have javascript enabled, the javascript_required.html file will be loaded, where you can inform your user that javascript is required. Often, links will just contain href="#" for the sake of simplicity, when you know for sure that your users will have javascript enabled. This practice is discouraged. It's always a good idea to put a local fall-back page that will be loaded for users with javascript disabled.
Sometimes, you want to conditionally follow a link. For example, if a user is navigating away from your form page and you first want to validate that nothing has changed. In this case, your onclick will call a function and it will return a valueitself to say whether the link should be followed.
<a href="/" onClick="return validate();">Home</a> function validate() { return prompt("Are you sure you want to exit this page?"); }
In this case, the validate() function should always return either true or false. True if the user should be allowed to navigate back to the home page, or false if the link should not be followed. This example prompts the user for confirmation, then returns true or false, depending on if the user clicked OK or Cancel.
Below are examples of things NOT to do. If you see code like this in your pages, it is not correct and should be fixed.
<a href="javascript:doSomething()">link</a> <a href="#" onClick="doSomething()">link</a> <a href="#" onClick="javascript:doSomething();">link</a> <a href="#" onClick="javascript:doSomething(); return false;">link</a>
Use The Unary + Operator To TypeConvert To Number
In javascript, the + operator is used for both addition and concatenation. This can cause problems when adding up form field values, for example, since javascript is a non-typed language. Form field values will be treated as strings, and if you + them together, javascript will treat it as concatenation instead of addition.
<form name="myform" action="[url]"> <input type="text" name="val1" value="1"> <input type="text" name="val2" value="2"> </form> function total() { var theform = document.forms["myform"]; var total = theform.elements["val1"].value + theform.elements["val2"].value; alert(total); // This will alert "12", but what you wanted was 3! }
To fix this problem, Javascript needs a hint to tell it to treat the values as numbers, rather than strings. You can use the unary + operator to convert the string value into a number. Prefixing a variable or expression with + will force it to evaluate as a number, which can then be successfully used in a math operation.
function total() { var theform = document.forms["myform"]; var total = (+theform.elements["val1"].value) + (+theform.elements["val2"].value); alert(total); // This will alert 3 }
Avoid document.all
document.all was introduced by Microsoft in IE and is not a standard javascript DOM feature. Although many newer browsers do support it to try to support poorly-written scripts that depend on it, many browsers do not.
There is never a reason to use document.all in javascript except as a fall-back case when other methods are not supported and very early IE support (<5.0) is required. You should never use document.all support as a way to determine if the browser is IE, since other browsers also now support it.
if (document.getElementById) { var obj = document.getElementById("myId"); } else if (document.all) { var obj = document.all("myId"); }
Some rules for using document.all are
- Always try other standard methods first
- Only fall back to using document.all as a last resort
- Only use it if you need to support IE versions earlier than 5.0
- Always check that it is supported with "if (document.all) { }" around the block where you use it.
Don't Use HTML Comments In Script Blocks
In the ancient days of javascript (1995), some browsers like Netscape 1.0 didn't have any support or knowledge of the script tag. So when javascript was first released, a technique was needed to hide the code from older browsers so they wouldn't show it as text in the page. The 'hack' was to use HTML comments within the script block to hide the code.
<script language="javascript"> <!-- // code here //--> </script>
No browsers in common use today are ignorant of the <script> tag, so hiding of javascript source is no longer necessary. In fact, it can be considered harmful for the following reasons:
- Within XHTML documents, the source will actually be hidden from all browsers and rendered useless
- -- is not allowed within HTML comments, so any decrement operations in script are invalid
Avoid Cluttering The Global Namespace
Global variables and functions are rarely required. Using globals may cause naming conflicts between javascript source files and cause code to break. For this reason, it is a good practice to encapsulate functionality within a single global namespace.
There are several ways to accomplish this task, some of which are much more complicated than others. The simplest approach is to create a single global object and assign properties and methods to this object.
var MyLib = {}; // global Object cointainer MyLib.value = 1; MyLib.increment = function() { MyLib.value++; } MyLib.show = function() { alert(MyLib.value); } MyLib.value=6; MyLib.increment(); MyLib.show(); // alerts 7
Namespaces can also be created using Closures, and Private Member Variables can also be simulated in javascript.
Avoid sync "Ajax" calls
When making "Ajax" requests, you may choose either async or sync mode. Async mode runs the request in the background while other browser activities can continue to process. Sync mode will wait for the request to return before continuing.
Requests made with sync mode should be avoided. These requests will cause the browser to lock up for the user until the request returns. In cases where the server is busy and the response takes a while, the user's browser (and maybe OS) will not allow anything else to be done. In cases where a response is never properly received, the browser may continue to block until the request is timed out.
If you think that your situation requires sync mode, it is most likely time to re-think your design. Very few (if any) situations actually require Ajax requests in sync mode.
Use JSON
When storing data structures as plain text or sending/retrieving data structures via Ajax, use JSON instead of XML when possible. JSON (JavaScript Object Notation) is a more compact and efficient data format, and is language-neutral.
Use Correct <script> Tags
The LANGUAGE attribute is deprecated in the <script> tag. The proper way to create a javascript code block is:
<script type="text/javascript"> // code here </script>