Narasimhan's Blogs
Thursday, October 5, 2023
Wednesday, January 21, 2015
Javascript & JQuery Best Practice and standards
1. Introduction
JavaScript has long been the
subject of many heated debates about whether it is possible to use it while
still adhering to best practices regarding accessibility and standards
compliance.
The answer to this question
is still unresolved, however, the emergence of JavaScript frameworks like
jQuery has provided the necessary tools to create beautiful websites without
having to worry (as much) about accessibility issues.
Obviously there are cases
where a JavaScript solution is not the best option. The rule of thumb here is:
use DOM scripting to enhance functionality, not create it.
1.1 Purpose
The
purpose of this document is to provide coding style standards for the
development of source code written in Javascript. Adhering to a coding style standard
is an industry proven best-practice for making team development more efficient
and application maintenance more cost-effective. While not comprehensive, these
guidelines represent the minimum level of standardization expected in the source
code of projects written in JQuery.
1.2 Scope
This document only applies to the Javascript for JQuery
Library .
1.3 Document Conventions
Example code is shown using the Code font and shows syntax
as it would be color coded in Visual Studio’s code editor.
1.4 Purpose of coding standards and best practices
To develop reliable and maintainable applications, you
must follow coding standards and best practices.
The naming conventions, coding standards and best
practices described in this document are compiled from our own experience and
by referring to various Microsoft and non-Microsoft guidelines.
1.5 How to follow the standards across the team
If the team is with different skills and tastes, it is
going to have a tough time convincing everyone to follow the same standards.
The best approach is to have a team meeting and developing own standards
document. This document is used as a template.
Distribute a copy of this document well ahead of the
coding standards meeting. All members should come to the meeting prepared to
discuss pros and cons of the various points in the document. Make sure you have
a manager present in the meeting to resolve conflicts.
Discuss all points in the document. Everyone may have a
different opinion about each point, but at the end of the discussion, all
members must agree upon the standard you are going to follow. Prepare a new
standards document with appropriate changes based on the suggestions from all
of the team members. Print copies of it and post it in all workstations.
After you start the
development, there must schedule code review meetings to ensure that everyone
is following the rules. 3 types of code reviews are recommended:
- Peer review – another team member review the code to ensure that the code follows the coding standards and meets requirements. This level of review can include some unit testing also. Every file in the project must go through this process.
- Architect review – the architect of the team must review the core modules of the project to ensure that they adhere to the design and there is no “big” mistakes that can affect the project in the long run.
- Group review – randomly select one or more files and conduct a group review once in a week. Distribute a printed copy of the files to all team members 30 minutes before the meeting. Let them read and come up with points for discussion. In the group review meeting, use a projector to display the file content in the screen. Go through every sections of the code and let every member give their suggestions on how could that piece of code can be written in a better way.
2. jQuery / JavaScript Best Practices
2.1 Unobtrusive DOM Scripting
While
the term “DOM scripting” really just refers to the use of scripts (in this
case, Javascripts) to access the Document Object Model, it has widely become
accepted as a way of describing what should really be called “unobtrusive DOM
scripting”—basically, the art of adding JavaScript to your page in such a way
that if there were NO JavaScript, the page would still work (or at least
degrade gracefully). In the website world, our DOM scripting is done using
JavaScript.
The Bottom Line: Accessible, Degradable Content
The aim
of any web producer, designer or developer is to create content that is
accessible to the widest range of audience. However, this has to be carefully
balanced with design, interactivity and beauty. Using the theories set out in
this article, designers, developers and web producers will have the knowledge
and understanding to use jQuery for DOM scripting in an accessible and
degradable way; maintaining content that is beautiful, functional AND
accessible.
2.2 Unobtrusive DOM Scripting?
In an
ideal world, websites would have dynamic functionality AND effects that degrade
well. What does this mean? It would mean finding a way to include, say, a
snazzy JavaScript Web 2.0 animated sliding news ticker widget in a web page,
while still ensuring that it fails gracefully if a visitor’s browser can’t (or
won’t) run Javascripts.
The
theory behind this technique is quite simple: the ultimate aim is to use
JavaScript for non-invasive, “behavioural” elements of the page. JavaScript is
used to add or enhance interactivity and effects. The primary rules for DOM
scripting follow.
2.2.1 Rule #1: Separate JavaScript Functionality
Separate
JavaScript functionality into a “behavioural layer,” so that it is separate
from and independent of (X) HTML and CSS. (X)HTML is the markup, CSS the
presentation and JavaScript the behavioural layer. This means storing ALL
JavaScript code in external script files and building pages that do not rely on JavaScript to be usable.
For a
demonstration, check out the following code snippets:
Bad markup:
Never
include JavaScript events as inline attributes. This practice should be
completely wiped from your mind.
<a onclick="doSomething()" href="#">Click!</a>
Good markup:
All JavaScript behaviours
should be included in external script files and linked to the document with a
<script> tag in the head of the page. So, the anchor tag would appear
like this:
<a href="backuplink.html" class="doSomething">Click!</a>
And the JavaScript inside the
myscript.js file would contain something like this:
$('a.doSomething').click(function () {
// Do something here!
alert('You
did something, woo hoo!');
});
The click() method in jQuery allows us to easily
attach a click event to the result(s) of our selector. So the code will select
all of the <a> tags of class “doSomething” and attach a click event that
will call the function. In practice, this
In Rule #2 there is a further
demonstration of how a similar end can be achieved without inline JavaScript
code.
2.2.2 Rule #2: NEVER Depend on JavaScript
To be
truly unobtrusive, a developer should never rely on JavaScript support to
deliver content or information. It’s fine to use JavaScript to enhance the
information, make it prettier, or more interactive—but never assume the user’s
browser will have JavaScript enabled. This rule of thumb can in fact be applied
to any third-party technology, such as Flash or Java. If it’s not built into
every web browser (and always enabled), then be sure that the page is still
completely accessible and usable without it.
Bad markup:
The
following snippet shows JavaScript that might be used to display a “Good
morning” (or “afternoon”) message on a site, depending on the time of day.
(Obviously this is a rudimentary example and would in fact probably be achieved
in some server-side scripting language).
<script language="javascript">
var now = new Date();
if (now.getHours() < 12)
document.write('Good Morning!');
else
document.write('Good Afternoon!');
</script>
This inline script is bad because if the target
browser has JavaScript disabled, NOTHING will be rendered, leaving a gap in the
page. This is NOT graceful degradation. The non-JavaScript user is missing out
on our welcome message.
A semantically correct and
accessible way to implement this would require much simpler and more readable
(X)HTML, like:
<p title="Good Day Message">Good Morning!</p>
By including the “title”
attribute, this paragraph can be selected in jQuery using a selector like the
one in the following JavaScript snippet:
var now = new Date();
if (now.getHours() >= 12) {
var goodDay = $('p[title="Good
Day Message"]');
goodDay.text('Good Afternoon!');
}
The
beauty here is that all the JavaScript lives in an external script file and the
page is rendered as standard (X)HTML, which means that if the JavaScript isn’t
run, the page is still 100% semantically pure (X)HTML—no JavaScript cruft. The
only problem would be that in the afternoon, the page would still say “Good
morning.” However, this can be seen as an acceptable degradation.
2.2.3 Rule #3: Semantic and Accessible Markup Comes First
It is
very important that the (X)HTML markup is semantically structured. (While it is
outside the scope of this article to explain why, see the links below for
further reading on semantic markup.) The general rule here is that if the
page’s markup is semantically structured, it should follow that it is also
accessible to a wide range of devices. This is not always true, though, but it
is a good rule of thumb to get one started.
Semantic
markup is important to unobtrusive DOM scripting because it shapes the path the
developer will take to create the DOM scripted effect. The FIRST step in
building any jQuery-enhanced widget into a page is to write the markup and make
sure that the markup is semantic. Once this is achieved, the developer can then
use jQuery to interact with the semantically correct markup (leaving an (X)HTML
document that is clean and readable, and separating the behavioural layer).
Terrible markup:
The following snippet shows a
typical list of items and descriptions in a typical (and terribly UNsemantic)
way.
<table>
<tr>
<td onclick="doSomething();">First Option</td>
<td>First option description</td>
</tr>
<tr>
<td onclick="doSomething();">Second Option</td>
<td>Second option description</td>
</tr>
</table>
Bad markup:
The following snippet shows a
typical list of items and descriptions in a more semantic way. However, the
inline JavaScript is far from perfect.
<dl>
<dt onclick="doSomething();">First Option</dt>
<dd>First option description</dd>
<dt onclick="doSomething();">Second Option</dt>
<dd>Second option description</dd>
</dl>
Good markup:
This snippet shows how the
above list should be marked up. Any interaction with JavaScript would be
attached at DOM load using jQuery, effectively removing all behavioural markup
from the rendered (X)HTML.
<dl id="OptionList">
<dt>First Option</dt>
<dd>First option description</dd>
<dt>Second Option</dt>
<dd>Second option description</dd>
</dl>
The <id> of
“OptionList” will enable us to target this particular definition list in jQuery
using a selector.
2.3 Understanding jQuery for Unobtrusive DOM Scripting
This section will explore
three priceless tips and tricks for using jQuery to implement best practices
and accessible effects.
The first step to unobtrusive
DOM scripting (at least in jQuery and Prototype) is using selectors. Selectors
can (amazingly) select an element out of the DOM tree so that it can be
manipulated in some way.
If you’re familiar with CSS
then you’ll understand selectors in jQuery; they’re almost the same thing and
use almost the same syntax. jQuery provides a special utility function to
select elements. It is called $.
A set of very simple
examples of jQuery selectors:
$(document); //
Activate jQuery for object
$('#mydiv') // Element with ID "mydiv"
$('p.first') // P tags with class first.
$('p[title="Hello"]') // P tags with title
"Hello"
$('p[title^="H"]') // P tags title starting with
H
So, as the JavaScript
comments suggest:
- $(document);
The first option will apply the jQuery library methods to a DOM object (in this case, the document object). - $(‘#mydiv’)
The second option will select every <div> that has the <id> attribute set to “mydiv”. - $(‘p.first’)
The third option will select all of the <p> tags with the class of “first”. - $(‘p[title="Hello"]‘)
This option will select from the page all <p> tags that have a title of “Hello”. Techniques like this enable the use of much more semantically correct (X)HTML markup, while still facilitating the DOM scripting required to create complex interactions. - $(‘p[title^="H"]‘)
This enables the selection of all of the <p> tags on the page that have a title that starts with the letter H.
Get ready.
$(document).ready()
$(document).ready()
Traditionally JavaScript events were
attached to a document using an “onload” attribute in the <body> tag of
the page. Forget this practice. Wipe it from your mind.
jQuery provides us with a special utility on
the document object, called “ready”, allowing us to execute code ONLY after the
DOM has completely finished loading. This is the key to unobtrusive DOM
scripting, as it allows us to completely separate our JavaScript code from our
markup. Using
$(document).ready(), we can queue up a series of events and have
them execute after the DOM is initialized.
This means that we can create entire effects
for our pages without changing the markup for the elements in question.
Why
$(document).ready()
To demonstrate the beauty of this
functionality, let’s recreate the standard introduction to JavaScript: a “Hello
World” alert box.
The following markup shows how we might have
run a “Hello World” alert without jQuery:
Bad
markup:
<script language="javascript">
alert('Hello
World');
</script>
Good
markup:
Using this functionality in jQuery is
simple. The following code snippet demonstrates how we might call the age-old
“Hello World” alert box after our document has loaded. The true beauty of this
markup is that it lives in an external JavaScript file. There is NO impact on
the (X)HTML page.
$(document).ready(function () {
alert('Hello
World');
});
How it works
The $(document).ready() function takes a
function as its argument. (In this case, an anonymous function is created
inline—a technique that is used throughout the jQuery documentation.) The function
passed to $(document).ready() is called after the DOM has finished loading and
executes the code inside the function, in this case, calling the alert.
2.4 Dynamic CSS Rule Creation
One problem with many DOM
scripting effects is that they often require us to hide elements of the
document from view. This hiding is usually achieved through CSS. However, this
is less than desirable. If a user’s browser does not support JavaScript (or has
JavaScript disabled), yet does support CSS, then the elements that we hide
in CSS will never be visible, since our JavaScript interactions will not have
run.
The solution to this comes in
the form of a plugin for jQuery called cssRule, which allows us to use
JavaScript to easily add CSS rules to the style sheet of the document. This
means we can hide elements of the page using CSS—however the CSS is ONLY
executed IF JavaScript is running.
Bad markup:
HTML:
<h2>This is a heading</h2>
<p class="hide-me-first">
This is some information about the
heading.
</p>
CSS:
p.hide-me-first
{
display:
none;
}
Assuming that a paragraph
with the class of “hide-me-first” is going to first be hidden by CSS and then
be displayed by a JavaScript after some future user interaction, if the
JavaScript never runs the content will never be visible.
Good markup:
HTML:
<h2>This is a heading</h2>
<p class="hide-me-first">
This is some information about the
heading.
</p>
jQuery JavaScript:
$(document).ready(function{
jQuery.cssRule("p.hide-me-first", "display", "none");
});
$(function(){
});
Using a $(document).ready()
JavaScript here to hide the paragraph element means that if JavaScript is
disabled, the paragraphs won’t ever be hidden—so the content is still
accessible. This is the key reason for runtime, JavaScript-based, dynamic CSS
rule creation
2.5 Loading jQuery
Always try to use a CDN to include jQuery on your page.
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/jquery-2.1.1.min.js"
type="text/javascript"><\/script>')</script>
If possible, keep all your
JavaScript and jQuery includes at the bottom of your page
1.
What version to use?
o
DO NOT use jQuery version 2.x if you support Internet Explorer
6/7/8.
o
For new web-apps, if you do not have any plugin compatibility
issue, it's highly recommended to use the latest jQuery version.
o
When loading jQuery from CDN's, always specify the complete
version number you want to load (Example: 1.11.0 as opposed
to 1.11 or just 1).
o
DO NOT load multiple jQuery versions.
o
DO NOT use jquery-latest.js from jQuery CDN.
2. If you are using other
libraries like Prototype, MooTools, Zepto etc. that uses $ sign as well, try not to use $ for calling jQuery
functions and instead use jQuery simply. You can return control
of $ back to the other
library with a call to $.noConflict().
2.6 jQuery Variables
1. All variables that are used to
store/cache jQuery objects should have a name prefixed with a $.
2.
Always cache your jQuery selector returned objects in variables
for reuse.
var $myDiv = $("#myDiv");
$myDiv.click(function(){...});
3.
Use camel case for
naming variables
2.7 Selectors
1. Use ID selector whenever
possible. It is faster because they are handled using document.getElementById().
2. When using class selectors,
don't use the element type in your selector.
var
$products = $("div.products"); //
SLOW
var
$products = $(".products"); //
FAST
3. Use find for Id->Child nested
selectors. The .find() approach is faster because the first selection is handled
without going through the Sizzle selector engine.
// BAD, a nested query for Sizzle selector enginea
var
$productIds = $("#products
div.id");
// GOOD, #products is already selected by
document.getElementById() so only div.id needs to go through Sizzle selector
engine
var
$productIds = $("#products").find("div.id");
4.
Be specific on the right-hand side of your selector, and less
specific on the left.
// Unoptimized
$("div.data .gonzalez");
// Optimized
$(".data td.gonzalez");
5. Avoid
Excessive Specificity.More Info,Performance Comparison
$(".data table.attendees
td.gonzalez");
// Better: Drop the middle if
possible.
$(".data td.gonzalez");
6. Give
your Selectors a Context.
// SLOWER because it has to traverse the whole DOM for .class
$('.class');
// FASTER because now it only looks under class-container.
$('.class', '#class-container');
7. Avoid
Universal Selectors
$('div.container > *'); //
BAD
$('div.container').children(); // BETTER
8. Avoid
Implied Universal Selectors. When you leave off the selector, the universal
selector (*) is still implied.
$('div.someclass :radio'); //
BAD
$('div.someclass input:radio'); //
GOOD
9. Don’t
Descend Multiple IDs or nest when selecting an ID. ID-only selections are
handled using document.getElementById() so don't mix
them with other selectors.
$('#outer #inner'); //
BAD
$('div#inner'); //
BAD
$('.outer-container #inner'); //
BAD
$('#inner'); //
GOOD, only calls document.getElementById()
2.8 DOM Manipulation
1. Always
detach any existing element before manipulation and attach it back after
manipulating it.
var
$myList = $("#list-container
> ul").detach();
//...a lot of complicated things on $myList
$myList.appendTo("#list-container");
2. Use
string concatenation or array.join() over .append(). More Info
Performance comparison: http://jsperf.com/jquery-append-vs-string-concat
Performance comparison: http://jsperf.com/jquery-append-vs-string-concat
// BAD
var
$myList = $("#list");
for(var i = 0; i < 10000; i++){
$myList.append("<li>"+i+"</li>");
}
// GOOD
var
$myList = $("#list");
var list
= "";
for(var i = 0; i < 10000; i++){
list += "<li>"+i+"</li>";
}
$myList.html(list);
// EVEN FASTER
var
array = [];
for(var i = 0; i < 10000; i++){
array[i] =
"<li>"+i+"</li>";
}
$myList.html(array.join(''));
3. Don’t
Act on Absent Elements.
// BAD: This runs three functions before it realizes there's
nothing in the selection
$("#nosuchthing").slideUp();
// GOOD
var
$mySelection = $("#nosuchthing");
if ($mySelection.length) {
$mySelection.slideUp();
}
2.9 Events
1. Use
only one Document Ready handler per page. It makes it easier to debug and keep
track of the behavior flow.
2. DO
NOT use anonymous functions to attach events. Anonymous functions are difficult
to debug, maintain, test, or reuse. More Info
$("#myLink").on("click", function(){...}); // BAD
// GOOD
function
myLinkClickHandler(){...}
$("#myLink").on("click", myLinkClickHandler);
3. Document
ready event handler should not be an anonymous function. Once again, anonymous
functions are difficult to debug, maintain, test, or reuse.
$(function(){ ... });
// BAD: You can never
reuse or write a test for this function.
// GOOD
$(initPage); //
or $(document).ready(initPage);
function
initPage(){
// Page load event where you can initialize values and call
other initializers.
}
4. Document
ready event handlers should be included from external files and inline
JavaScript can be used to call the ready handle after any initial setup.
<script
src="my-document-ready.js"></script>
<script>
// Any global variable set-up that
might be needed.
$(document).ready(initPage); // or $(initPage);
</script>
5. DO
NOT use behavioral markup in HTML (JavaScript inlining), these are debugging
nightmares. Always bind events with jQuery to be consistent so it's easier to
attach and remove events dynamically.
<a id="myLink" href="#" onclick="myEventHandler();">my
link</a> <!-- BAD -->
$("#myLink").on("click", myEventHandler); // GOOD
6. When
possible, use custom namespace for
events. It's easier to unbind the exact event that you attached without
affecting other events bound to the DOM element.
$("#myLink").on("click.mySpecialClick", myEventHandler); // GOOD
// Later on, it's easier to unbind just your click event
$("#myLink").unbind("click.mySpecialClick");
2.10 Ajax
1. Avoid
using .getJson() or .get(), simply use the
$.ajax() as that's what gets called internally.
2. DO
NOT use http requests on https sites. Prefer
schemaless URLs (leave the protocol http/https out of your
URL)
3. DO
NOT put request parameters in the URL, send them using data object setting.
// Less readable...
$.ajax({
url: "something.php?param1=test1¶m2=test2",
....
});
// More readable...
$.ajax({
url: "something.php",
data: { param1: test1, param2: test2 }
});
4. Try
to specify the dataType setting so it's easier to know what kind of data
you are working with. (See Ajax Template example below)
5. Use
Delegated event handlers for attaching events to content loaded using Ajax.
Delegated events have the advantage that they can process events from
descendant elements that are added to the document at a later time (example
Ajax).
$("#parent-container").on("click", "a", delegatedClickHandlerForAjax);
6. Use
Promise interface:
$.ajax({
... }).then(successHandler,
failureHandler);
// OR
var
jqxhr = $.ajax({ ... });
jqxhr.done(successHandler);
jqxhr.fail(failureHandler);
7. Sample
Ajax Template:
var
jqxhr = $.ajax({
url: url,
type: "GET", // default is GET but you can use
other verbs based on your needs.
cache: true, // default is true, but false for
dataType 'script' and 'jsonp', so set it on need basis.
data: {},
// add your request
parameters in the data object.
dataType: "json", // specify the dataType for future
reference
jsonp: "callback",
// only specify this to
match the name of callback parameter your API is expecting for JSONP requests.
statusCode: {
// if you want to
handle specific error codes, use the status code mapping settings.
404: handler404,
500: handler500
}
});
jqxhr.done(successHandler);
jqxhr.fail(failureHandler);
2.11 Effects and Animations
1.
Adopt a restrained and consistent approach to implementing animation
functionality.
2.
DO NOT over-do the animation effects until driven by the UX
requirements.
o
Try to use simeple show/hide, toggle and slideUp/slideDown
functionality to toggle elements.
o Try to use predefined
animations durations of "slow", "fast" or 400 (for medium).
2.12 Plugins
1.
Always choose a plugin with good support, documentation, testing
and community support.
2.
Check the compatibility of plugin with the version of jQuery
that you are using.
3.
Any common reusable component should be implemented as a jQuery
plugin
2.13 Chaining
1.
Use chaining as an alternative to variable caching and multiple
selector calls.
$("#myDiv").addClass("error").show();
2. Whenever the chain
grows over 3 links or gets complicated because of event assignment, use
appropriate line breaks and indentation to make the code readable.
$("#myLink")
.addClass("bold")
.on("click",
myClickHandler)
.on("mouseover",
myMouseOverHandler)
.show();
3. For long chains it is
acceptable to cache intermediate objects in a variable.
2.14 Use Tags Before Classes
The second fastest selector in jQuery is
the Tag selector (
$('head')).
Again, this is
because it maps to a native JavaScript method, getElementsByTagName()
<div id="content">
<form method="post" action="/">
<h2>Traffic Light</h2>
<ul id="traffic_light">
<li><input type="radio" class="on" name="light" value="red" /> Red</li>
<li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>
<li><input type="radio" class="off" name="light" value="green" /> Green</li>
</ul>
<input class="button" id="traffic_button" type="submit" value="Go" />
</form>
</div>
Always prefix a class with a tag name
(and remember to descend from an ID):
var active_light = $('#traffic_light
input.on');
Note: The class
selector is among the slowest selectors in jQuery; in IE it loops through the
entire DOM. Avoid using it whenever possible. Never prefix an ID
with a tag name. For example, this is slow because it will loop through all
<div> elements looking for
the ‘content’ ID:
var active_light = $('#traffic_light
input.on');
Along the same lines, it is redundant to
descend from multiple IDs:
var traffic_light = $('#content #traffic_light');
2.15 Cache jQuery Objects
Get in the habit of saving your jQuery
objects to a variable (much like our examples above). For example, never
(eeeehhhhver) do this:
$('#traffic_light input.on').bind('click', function(){...});
$('#traffic_light input.on').css('border', '3px dashed yellow');
$('#traffic_light input.on').css('background-color', 'orange');
$('#traffic_light input.on').fadeIn('slow');
Instead, first save the object to a
local variable, and continue your operations:
var $active_light = $('#traffic_light input.on');
Tip: Since we want to
remember that our local variable is a jQuery wrapped set, we are using $ as a
prefix.
Remember, never repeat a
jQuery selection operation more than once in your application.
Storing jQuery results for
later
If you intend to use the jQuery result
object(s) in another part of your program, or should your function execute more
than once, cache it in an object with a global scope. By defining a global
container with jQuery results, we can reference them from within other
functions:
//
Define an object in the global scope (i.e. the window object)
window.$my
=
{
//
Initialize all the queries you want to use more than once
head : $('head'),
traffic_light : $('#traffic_light'),
traffic_button : $('#traffic_button')
};
function do_something()
{
// Now
you can reference the stored results and manipulate them
var script = document.createElement('script');
$my.head.append(script);
// When
working inside functions, continue to save jQuery results
// to
your global container.
$my.cool_results = $('#some_ul li');
$my.other_results = $('#some_table td');
// Use
the global functions as you would a normal jQuery result
$my.other_results.css('border-color', 'red');
$my.traffic_light.css('border-color', 'green');
}
2.16 Harness the Power of Chaining
The previous example can also be
accomplished like this:
var $active_light = $('#traffic_light
input.on');$active_light.bind('click', function(){...})
.css('border', '3px dashed yellow')
.css('background-color', 'orange')
.fadeIn('slow');
This allows us to write less code,
making our JavaScript more lightweight.
2.17 Use Sub-queries
jQuery allows us to run additional
selector operations on a wrapped set. This reduces performance overhead on
subsequent selections since we already grabbed and stored the parent object in
a local variable.
<div id="content">
<form method="post" action="/">
<h2>Traffic Light</h2>
<ul id="traffic_light">
<li><input type="radio" class="on" name="light" value="red" /> Red</li>
<li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>
<li><input type="radio" class="off" name="light" value="green" /> Green</li>
</ul>
<input class="button" id="traffic_button" type="submit" value="Go" />
</form>
</div>
For example, we can leverage sub-queries
to grab the active and inactive lights and cache them for later manipulation.
var $traffic_light = $('#traffic_light'),
$active_light = $traffic_light.find('input.on'),
$inactive_lights = $traffic_light.find('input.off');
Tip: You can declare
multiple local variables by separating them with commas – save those bytes!
2.18 Limit Direct DOM Manipulation
The basic idea here is to create exactly
what you need in memory, and then
update the DOM. This is not a jQuery best practice, but a must for efficient
JavaScript. Direct DOM manipulation is slow. For example, if you need to
dynamically create a list of elements, do not do this:
var top_100_list = [...], //
assume this has 100 unique strings
$mylist = $('#mylist'); // jQuery selects our <ul> element
for (var i=0, l=top_100_list.length; i<l; i++)
{
$mylist.append('<li>' +
top_100_list[i] + '</li>');
}
Instead, we want to create the entire
set of elements in a string before inserting into the DOM:
var top_100_list = [...], // assume this has 100 unique strings
$mylist
= $('#mylist'),
// jQuery selects our <ul> element
top_100_li
= ""; // This will store our list
items
for (var i=0, l=top_100_list.length;
i<l; i++)
{
top_100_li += '<li>' + top_100_list[i] + '</li>';
}
$mylist.html(top_100_li);
Even faster, we should always wrap many elements
in a single parent node before insertion:
var top_100_list =
[...], // assume this has 100 unique strings
$mylist = $('#mylist'), // jQuery selects our
<ul> element
top_100_ul = '<ul
id="#mylist">'; // This will store our entire unordered list
for (var i=0, l=top_100_list.length;
i<l; i++)
{
top_100_ul += '<li>' + top_100_list[i] + '</li>';
}
top_100_ul
+= '</ul>'; // Close our unordered list
$mylist.replaceWith(top_100_ul);
If you do the above and are still
concerned about performance:
- Give jQuery’s
clone()method a try. This creates a copy of the node tree, which you can manipulate “off-line” and then insert back in when you are ready. - Use DOM DocumentFragments. As the creator of jQuery points out, they perform much better than direct DOM manipulation. The idea would be to create what you need (similar to what we did above with a string), and use the jQuery insert or replace methods.
2.19 Leverage Event Delegation (a.k.a. Bubbling)
Unless told otherwise,
every event (e.g. click, mouseover, etc.) in JavaScript “bubbles” up the DOM
tree to parent elements. This is incredibly useful when we want many elements
(nodes) to call the same function. Instead of binding an event listener
function to many nodes—very inefficient—you can bind it once to their parent, and have it
figure out which node triggered the event. For example, say we are developing a
large form with many inputs, and want to toggle a class name when selected. A
binding like this is inefficient:
$('#myList
li').bind('click', function(){
$(this).addClass('clicked');
// do stuff
});
Instead, we should
listen for the click event at the parent level:
$('#myList').bind('click', function(e){
var target = e.target, // e.target grabs the node that triggered the event.
$target = $(target); // wraps the node in a jQuery object
if (target.nodeName === 'LI') {
$target.addClass('clicked');
// do stuff
}
});
The parent node acts as a dispatcher and
can then do work based on what target element triggered the event. If you find
yourself binding one event listener to many elements, you are doing something
wrong (and slow).
2.20 Eliminate Query Waste
Although jQuery fails nicely if it does
not find any matching elements, it still takes time to look for them. If you
have one global JavaScript for your entire site, it may be tempting to throw
every one of your jQuery functions into
$(document).ready(function(){ // all my glorious code }).
Don’t you dare.
Only run functions that are applicable to the page. The most efficient way to
do this is to use inline initialization functions so your template has full
control over when and where JavaScript executes. For example, in your “article”
page template, you would include the following code before the body close:
<script type="text/javascript>
mylib.article.init();
</script>
</body>
If your page template includes any
variety of modules that may or may not be on the page, or for visual reasons
you need them to initialize sooner, you could place the initialization function
immediately after the module.
<ul id="traffic_light">
<li><input type="radio" class="on" name="light" value="red" /> Red</li>
<li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>
<li><input type="radio" class="off" name="light" value="green" /> Green</li>
</ul>
<script type="text/javascript>
mylib.traffic_light.init();
</script>
Your Global JS library would look something
like this:
var mylib =
{
article_page:
{
init: function () {
// Article page specific jQuery
functions.
}
},
traffic_light:
{
init: function () {
// Traffic light specific jQuery
functions.
}
}
}
2.21 Defer
to $(window).load
There is a temptation
among jQuery developers to hook everything into the
$(document).ready pseudo-event. After
all, it is used in most examples you will find. Although $(document).ready is incredibly useful,
it occurs during page render while objects are still downloading. If you notice
your page stalling while loading, all those $(document).ready functions could be the
reason why. You can reduce CPU utilization during the page load by binding your
jQuery functions to the $(window).load
event, which
occurs after all objects called by the HTML (including <iframe> content) have downloaded.
$(window).load(function () {
// jQuery functions to initialize after the page has
loaded.
});
Superfluous functionality such as drag
and drop, binding visual effects and animations, pre-fetching hidden images,
etc., are all good candidates for this technique.
2.22 Miscellaneous
1.
Use Object literals for parameters.
Bad
markup:
$myLink.attr("href", "#").attr("title", "my link").attr("rel", "external");
Good
markup:
$myLink.attr({
href: "#",
title: "my
link",
rel: "external"
});
2.
Do not mix CSS with jQuery.
Bad
markup:
$("#mydiv").css({'color':red, 'font-weight':'bold'});
Good
markup:
.error { color: red; font-weight: bold; } /* CSS */
$("#mydiv").addClass("error");
3.
DO NOT use Deprecated Methods. It is always important to keep an
eye on deprecated methods for each new version and try avoid using them.
4.
Always Descend From an #id
HTML
Markup
<div id="content">
<form method="post" action="/">
<h2>Traffic Light</h2>
<ul id="traffic_light">
<li><input type="radio" class="on" name="light" value="red" /> Red</li>
<li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>
<li><input type="radio" class="off" name="light" value="green" /> Green</li>
</ul>
<input class="button" id="traffic_button" type="submit" value="Go" />
</form>
</div>
Bad
markup:
var traffic_button = $('#content
.button');
Good
markup:
var traffic_button = $('#traffic_button');
Selecting
Multiple Elements
Once we start talking about selecting
multiple elements, we are really talking about DOM traversal and looping,
something that is slow. To minimize the performance hit, always descend from the closest parent
ID:
var traffic_lights = $('#traffic_light
input');
5.
Combine jQuery with native JavaScript when needed. See the
performance difference for the example given below:
$("#myId"); // is still little slower
than...
document.getElementById("myId");
6.
Avoid
unnecessary upgrades of jQuery, unless there are features, which you require or
some plugins require. Stick to one version from the beginning. (ex. 1.5.1)
7.
Avoid
writing event handler attributes (onclick etc.), instead use live or bind
methods to attach the respective event handlers.
8.
Avoid
mixing javascript code with jQuery code.
9.
Cache
any jQuery selector, if reused in multiple statements.
Ex. Avoid $("#x").val(); $("#x").attr("align","right");
Ex. Avoid $("#x").val(); $("#x").attr("align","right");
Use var $x = $("#x"); $x.val();$x.attr("align","right");
10. Use find method, instead of building a
complex jQuery selector.
11. Keep your code safe, by using noConflict()
method or by passing jQuery.
Ex.(function ($) { })(jQuery); or by wrapping it in a ready method.
Ex.(function ($) { })(jQuery); or by wrapping it in a ready method.
12. Avoid declaring new jQuery selectors within
foreach, instead declare them outside and reuse it inside the loop.
13. Use $.ajax instead of $.get or $.getJSON,
because internally they call $.ajax.
14. Use JSON formats for communications.
15. Ensure that you move your jQuery codes to a
separate javascript file, instead of inline scripts.
16. Compress javascript files for better
performance.
17. Combine multiple css() calls or attr() calls
into one. In case of attributes, you could also pass them as shown below:
$('</a>', {
id : 'myId',
className : 'myClass',
href : 'mytest.html'
});
18. Avoid
misusing $(this).
Ex. Use this.id instead of $(this).attr('id');
Ex. Use this.id instead of $(this).attr('id');
- Usage of “use strict" Directive : The "use strict" directive is new in JavaScript 1.8.5 (ECMAScript version 5).It is not a statement, but a literal expression, ignored by earlier versions of JavaScript. The purpose of "use strict" is to indicate that the code should be executed in "strict mode”. With strict mode, you cannot, for example, use undeclared variables.
- Usage of “===” wherever
it is required , The main difference between “==” and “===” , “
==” operator will compare for equality after doing any necessary type conversions. The===operator will not do the conversion, so if two values are not the same type===will simply returnfalse. It's this case where===will be faster, and may return a different result than==. In all other cases performance will be the same.
Subscribe to:
Posts (Atom)