I’ve seen a lot of curious (bordering on horrific) code in my life; and I’d say about half of it was written by me. If you don’t attest to the fact that you once wrote crap code then you’re either a liar or perhaps, have omnipotent powers!
Here’s a relatively small collection of what I see as bad JavaScript practices; in other words – a list of how to write really really ugly JavaScript that will break! I know it’s quite a negative angle to take but it’s easier than writing a post on best practices, plus it gives me a chance to vent! Many of these are things I’ve done in the past; I’ve since discovered the errors in my ways.
Non-constructor identifiers beginning with a capital letter
Variable names should only begin with a capital if they’re pointing to constructor functions. Doug Crockford has more:
There is a convention that all constructor functions are named with an initial capital, and that nothing else is spelled with an initial capital. This gives us a prayer that visual inspection can find a missing
new
.
Not following conventions will only get you in trouble! So, to be clear, this is all wrong:
var Color = 'red'; var SomeNumber = 234; var AnArray = [1,2,3]; var Foo = { bar: 123 }; // None of them should start with a capital letter! |
Only functions that are constructors should be identified with an initial captial letter, not regular functions. E.g.
/* This is a regular function. */ function sum() { var total = 0, i = 0, len = arguments.length; for ( ; i < len; ++i ) { total += arguments[i]; } return total; } /* This is a constructor function */ function Widget(innerText, styles) { this.innerText = innerText; this.cssStyles = styles; } |
Not using the var
statement to define variables
Not using the var
statement when defining variables for the first time is a very bad idea. These variables will become part of the global scope. This sucks because you won’t get any warning. Not only is it a bad practice but it can have a negative affect on performance, after-all, the further away a scope is the longer it takes to access. So, always use the var
statement:
foo = 3; // NO! var foo = 3; // YES |
Prefixing every new variable in a given scope with var
There’s no point in having several var
statements if you only need one:
var someVar1 = 'a'; var someVar2 = 'b'; var someVar3 = 'c'; // Much easier: var someVar1 = 'a', someVar2 = 'b', someVar3 = 'c'; |
Using non-strict comparison operators, and then comparing across different types
There are two classes of comparison operators; those that type-cast (==
/!=
) when required and those that don’t (===
/!==
). You don’t ever want to use the former!
1 == "1"; // true false == " nt "; // true [[],[]] == true; // true // ... Confused? |
See? It’s obviously a bad idea! Please only ever use strict-equality comparison operators; they’ll check that the two operands are identical, not just “equal”:
1 === "1"; // false false === " nt "; // false [[],[]] === true; // false // Just as expected! |
Not “caching” non-varying complex objects
This is vitally important; if you’re going to be repeatedly using an object you should name it and save it! A basic example:
function myFunction(arg) { var configObj = { option1: 123, option2: 456 }; // do stuff } |
configObj
is being created every time the function is executed. It doesn’t change on each call so there’s no point in having it in the function body. Here’s a better alternative:
var myFunction = (function(){ var configObj = { option1: 123, option2: 456 }; return function(arg) { // do stuff }; })(); |
Here we’re storing configObj
in a “private” scope only accessible to “child” scopes. It’s only being created one time so we’ve instantly shaved notable milliseconds off execution time!
Doing too much in a loop or recursive function
Doing too much in a loop can have terrible affects on performance. Every single thing you do inside a loop will add to its overall execution time and if this grows beyond about ~50ms (100ms max) the user will start noticing. There are a couple of small enhancements you can make to optimise your loops:
- Cache object properties before the loop:
for (var i = 0, len = array.length; i < len; ++i) {} /* Notice we're caching the "length" property so we don't have to check it on every iteration */
- Loop in reverse if possible:
var i = array.length; while (i--) { /* Combining the control condition and any control variable changes makes it much faster! */ }
Not understanding and therefore not appreciating the benefits in abstracting repeatedly utilized code versus having a wall of unreadable code
Urm, yeh, the title says it all really! Compare this:
document.getElementById('foo').style.border = '3px solid #FFF'; document.getElementById('foobar').style.border = '3px solid #FFF'; document.getElementById('foofoobar').style.border = '3px solid #FFF'; document.getElementById('foofoobarbar').style.border = '3px solid #FFF'; |
… to this:
var allMyFoos = '#foo, #foobar, #foofoobar, #foofoobarbar'; jQuery(allMyFoos).css('border', '3px solid #FFF'); |
(Yes, jQuery is a superb example of “abstracting repeatedly utilized code”)
Choosing terseness over readability
Readability is always better than terseness. There may be some situations where the more concise approach has other benefits but your main focus should normally be the readability of your code; not just to you, but to other developers. There’s no need to dumb-down your code though; readability is not the same as simplicity.
// 1: while (parent.nodeName.toLowerCase() !== 'div' && (p = parent.parentNode)); // 2: do { if (parent.nodeName.toLowerCase() === 'div') { break; } } while (parent = parent.parentNode); // 3: do if (parent.nodeName.toLowerCase() === 'div') break; while (parent = parent.parentNode); |
The above constructs all do the same thing but one is clearly more readable that the other two. Unfortunately with readability you gain (arguably) unnecessary cruft.
Not knowing what DRY is, or how to apply it to JavaScript
There are many different ways to express this concept but essentially it centers around not repeating yourself. A good example:
elemCollection[i].style.color = 'red'; elemCollection[i].style.backgroundColor = 'blue'; elemCollection[i].style.border = '2px solid #000'; elemCollection[i].style.paddingLeft = '3px'; elemCollection[i].style.marginTop = '3px'; elemCollection[i].style.fontSize = '1.2em'; elemCollection[i].style.fontStyle = 'italic'; |
The above mess can be expressed in a much cleaner way:
applyCSS(elemCollection[i], { color: 'red', backgroundColor: 'blue', border: '2px solid #000', paddingLeft: '3px', marginTop: '3px', fontSize: '1.2em', fontStyle: 'italic' }); |
(Using the following helper function:)
function applyCSS(el, styles) { for (var prop in styles) { if (!styles.hasOwnProperty || styles.hasOwnProperty(prop)) { el.style[prop] = styles[prop]; } } return el; } |
Commenting every line
As Jeff Atwood boldly said:
If your feel your code is too complex to understand without comments, your code is probably just bad. Rewrite it until it doesn’t need comments any more. If, at the end of that effort, you still feel comments are necessary, then by all means, add comments. Carefully.
We’ve all done it; commented almost every other line thinking that it brings a whole new level of clarity to our code, when it actually impedes readability and distracts from the code itself. Just try to limit how many comments you add; they’re not always as helpful as you think they are!
Using browser detection instead of feature detection
Feature-detection is future-proof. Browser-detection isn’t. It’s as simple as that!
If, for example, you needed to find out whether a browser supports the min-height CSS property, you could test it in the following way:
var minHeightSupport = (function(){ var aHeight, bHeight, doc = document, aDiv = document.createElement('div'), bDiv = document.createElement('div'); aDiv.style.position = bDiv.style.position = 'absolute'; doc.body.appendChild(aDiv); doc.body.appendChild(bDiv); bHeight = bDiv.clientHeight; doc.body.removeChild(bDiv); aDiv.style.minHeight = (bHeight+1) + 'px'; aHeight = aDiv.clientHeight; doc.body.removeChild(aDiv); return aHeight > bHeight; })(); |
Writing feature-tests can be a long and challenging exercise but in the end you should be left with something that’s very reliable! The above test goes something like this:
- Create two DIV elements, “aDiv” and “bDiv”.
- Absolutely position them so the page doesn’t shift.
- Append them both to the document.
- Save
bDiv
‘s current height then remove it from the document. - Set
aDiv
‘smin-height
property to one more thanbDiv
‘s height. - If
aDiv
‘s overall height is greater thanbDiv
‘s height thenmin-height
must be supported!
(The above function is just an example; I haven’t tested it sufficiently yet.)
Creating DOM elements within a loop
It’s slow; very slow actually. When you have no other choice then make sure you’re using a document fragment that will be inserted into the document later, don’t create and append individual DOM elements in to the actual document.
Regular slow approach:
for (var i = 0; i < 100; ++i) { elementInDocument.appendChild(document.createElement('div')); } |
Using a document fragment:
var fragment = document.createDocumentFragment(); for (var i = 0; i < 100; ++i) { fragment.appendChild(document.createElement('div')); } elementInDocument.appendChild(fragment); |
Using innerHTML
and array.join()
(even faster):
elementInDocument.innerHTML += Array(101).join('<div/>'); |
Using inline event handlers, YUCK!
Just don’t do it! Take the unobtrusive approach instead.
Big no-no! –
<a href="javascript:void doSomething();">Click</a> <!--OR--> <a href="#" onclick="return doSomething();">Click</a> |
Much better:
jQuery('element').click(doSomething); |
(I’m not saying you have to use jQuery but you should develop a couple of abstractions to make it easier to work with events and DOM elements – at the least)
Having long HTML strings in your JavaScript
They’re ugly and hard to maintain. Either use DOM methods or put the HTML elsewhere; maybe in a template file or somewhere in the document, in a comment node or a hidden element. Seriously, just look at this:
var widgetStructure = '<div id=' + widgetID + '>' + '<h2 id="' + widgetTitleID + '">' + widgetTitle + '</h2>' + '<div id="' + widgetContentID + '" style="' + widgetContentStyles + '">' + (function() { var paras = ''; for (var i = 0; i < paraItems.length; ++i) { paras += '<p>' + paraItems[i] + '</p>'; } return paras; })() + '</div>' + '</div>'; |
Yes, I have seen that done before; ugly as sin! Please don’t do it!
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
I like your article, I do not agree on commenting tho, This is not an absolute bad practice, commenting is considered a good practice in general if used in a good way.
I consider that even if you write comprehensible code, there is need for additional comments.
Really useful post, been getting more into javascript lately and I’ve spotted a few things that I’ve been doing wrong so this can only serve to help me ๐ Cheers.
@Cedric – I didn’t say “commenting” was a bad practice; I said “Commenting every line” was bad. The code itself should tell the viewer what’s going on; comments should be used for additional hints, and that’s it IMO. And, I do agree with you, commenting is a good practice, “if used in a good way” – the problem is, many people don’t use comments correctly.
@Mark – Glad it helped! ๐
Great post James, I’m by no means a Javascript expert but I’m trying to end my over-reliance on jQuery for basic tasks, and your posts, although sometimes a bit confusing to me always are a big help. Thanks.
I quite agree with “Having long HTML strings in your JavaScript” that was my favorite point.
There exists another bad practice I hate:
Great article, James. Lots of good stuff here.
“Having long strings in your JavaScript” and using hidden fields. Do you have an example of how I might store a long string in a hidden field that has many JavaScript variables that need to be substituted in to create the string. Thanks.
Just as a follow up to my previous comment, if there is a better approach then hidden fields to store long strings I would like to see it. I have approximately 50 quite complex JavaScript strings that each require between 5 and 10 values from input fields to complete the strings and I am currently storing these strings in JavaScript variables and substituting in the input field values to build the strings. Any recommendations on a better approach would be great. Thanks.
Good job, it will save me a lot and I hope others too….
Nice article. I disagree with the “use strict operators” part. I think every developer should know the differences, and use what’s appropriate. Sometimes, non-strict operators are just what you need, precisely due to their looseness. Also, given that a
if(!foo) { ... }
is essentially using a non-strict operator (if(foo == false) { ... }
), we all use non-strict operators frequently, even you. ๐Regarding document fragments, something always bugged me about them: Let’s suppose you want to create an unordered list. If you create the ul element (without adding it to the document), add all the <li>s to it and then add it to the document, how is that slower than adding the <ul> to a fragment and then doing almost the same thing? Or that rule about document fragments being faster doesn’t apply to such cases?
I agree with all but one. The “don’t use var for every variable” is a bit pointless in my opinion, since all it does is make the code more difficult to modify.
For example, if you have three variables as in your example, and you no longer need the middle one. If you used var for each of them, you just delete the declaration. If you didn’t, you need to edit all three lines, not to mention that you might still unintentionally just delete the line and forget to change the others if you’re not paying 100% attention.
Nice article. Only, some people may not like to use a framework. There are some valid reasons for that as well. In that case few of your points become invalid for those people. Yet, I think those are more advanced JS coders anyways. ๐
@Lea Verou: document fragment is just an abstract element container that you use if you are, for instance, creating multiple DIV elements that don’t have any container by themself. If you are constructing a UL list then it is perfectly valid to create a UL element, add LI elements to it, and then insert the UL element into the DOM.
@Jani Hartikainen: I agree with you, but still, don’t modify code if you are not paying 100% attention ๐
I agree with most of the points – I believe you’re talking about using strict comparison operators when comparing across types, which is absolutely correct (non-strict is fine when comparing against the same type, or just wanting a quick evaluation of true/false).
I see your point regarding excessive commenting, but rewriting “until it doesnโt need comments any more” is plain silly.
Also, “being inconsistent” would have been at the top of my list (not JS specific, obviously). I can forgive some bad practices when a developer is starting out, but there’s nothing worse than someone changing coding styles every few lines.
Nice article. I’m no javascript expert but using it more and more so some helpful stuff to read over there.
I have a wonderful solution for the “long HTML strings” issue.
It’s called JSHTML.
Ok, ok, it’s not actually mine solution.. ; )
https://j11y.io/javascript/introducing-jshtml/
I agree on all of them.
The caching example is great.
Just for the browser detection issue, I found that often in my daily job it’s too complicated to be practical. And also, most of the websites I code will sure be re-designed in a year or two. So futureproof is not so important.
But of course, for a javascript library (like jQuery) it’s a great feature.
Too Good a List..
Thanks !
Thanks for great post. Very useful points.
Thanks for the comments!
@Lea, good point, the fact that
if (foo){}
is the same asif (foo == true){}
totally slipped my mind!Document fragments are useful when you want to add a series of elements to the DOM without a container wrapping them (as Jan mentioned).
@Graham, I see your point. I agree; it wouldn’t make sense re-writing until your code is devoid of all comments. The trick is to comment correctly the first time round.
@Jani Hartikainen, I guess you’re right; I only mentioned it as more a space-saving tip. Using
var
repeatedly isn’t really a bad practice but it certainly is a waste of space.“Using inline event handlers, YUCK!”
I used to agree with that, but I have been enlightened and now i boldly Disagree.
I work on a platform that is customized by hundreds of thousands of site ownersdevelopers and serves many millions of of pages every day and the one thing you learn when you work on a platform this large and distributed is that not everyone using your site has the same processing power, browser, or internet connection.
Relying on window ready or document ready events to dynamically bind DOM events is not a reliable way to setup events. As a developer, you make sure things work on your browser (or test suite of browsers) but most times you cannot know who will be using your site.
For whatever reason. if the document is displayed and a user begins clicking around on your site before the “document ready” event fires, you will end up having unexpected behavior.
Using inline onclick events means that your events are ready as soon as the element is ready in the DOM and there is no additional waiting or processing needed to attach the event. When you need a site that really performs, this is the preferred method. Look around at some heavy ajax sites and you’ll see that most (if not all) of them use this approach.
@Bryan Migliorisi, I see where you’re coming from but the conclusion you’ve made, to use inline event handlers, is wrong. Using the DOM-ready event was never a good practice; it’s a tacky pseudo-event popularized out of convenience.
I have two suggestions:
You could abandon the DOM-ready/window-load event and simply place all DOM/event-altering scripts at the bottom of the document.
Or, (this is my preferred solution), use event delegation. Bind your click event to the
body
element (or any element), then you can detect the clicked target when the event propagates. This will also have the effect of further improving performance – since only one handler is being bound.Just because some “heavy ajax sites” use inline event handlers doesn’t mean it’s the right thing to do…
@Bryan
you’re right.. that’s a true problem, and there’s no other place where code run first than inline code
to avoid this problem in a recent project I hidden some buttons and showed them only after domready. but probably if I surrended to inline code it would be better..
@James
I should mention that we also rely heavily on event delegation, though for the sake of simpler code, we typically do not bind everything to body and instead bind to several common parent nodes.
Again relying on a footer script may not solve the issue either. If that HTTP request takes anything more than say 250ms you risk giving users a moment of time that they could click on elements and see unexpected results. Sounds like nothing, but you’d be surprised (we sure were).
Also, you’re right – just because some sites do something doesn’t make it the “right” way of doing it, but from experience I can tell you that in cases like mine and theirs, it is the one that makes the most sense.
Of course in every project you need to weigh your options – like everything else, there are always a few ways to attach DOM events but they each have pro’s and con’s that must be considered for your expected audience.
(BTW, great post)
@Bryan Migliorisi, I still think you could use event delegation to greater effect. Obviously one handler can get massively complicated but you could abstract all that confusion away… The good thing about this is that it could all be done before the DOM is completely loaded. Here’s an example abstraction: http://gist.github.com/171716
@Bryan & @James
if we load jQuery in the header, can we use jQuery.live for the same purpose?
@Valentino
Yea, jQuery.live() does the same thing, but I have read reports that it has some flaws in its implementation. I am not familiar enough with their code to speculate further.
I took a quick look at the event delegation code James posted and it seems pretty simple and well written. If you don’t need jQuery (or another library) I would recommend that you use James’ code.
I really like this post. A few suggestions:
– I think the term you can use when talking about appending elements is “reflow”; Smashing Mag had a good article on this today.
– My favorite bad practice that I’d wished you mentioned was the semi-colon insertion problem outlined by Douglas Crockford. So many people miss that one!
Really awesome! You have figured out a best practice guide for javascript, thanks for sharing your valuable knowledge with us.
Great write up, seeing what not to do, in addition to seeing what you should do is a great way to reinforce good habits.
Thanks for useful article
“Using innerHTML and array.join() (even faster): elementInDocument.innerHTML += Array(101).join(”);” โ this method has a huge drawback: it will erase all binded events for elements inside elementInDocument.
And I’m agree with Bryan Migliorisi: using inline events isn’t always a bad practice. They’re widely used by performance geeks in large heavy-loaded projects, because it’s the fastes way to add event on specified element than adding event listeners or delegating events.
James, nice post!
I disagree with you on the point of “Not caching non-varying complex objects”:
this is a case of code that can be optimized, but is not necessarily bad practice. I prefer my code to be focused on readability UNTIL performance becomes an issue. The syntax of the optimized case is less readable than the suboptimal case. (And hey, there’s a sensible use of comments: explain to other programmers why the object is outside the function!)
One minor detail: “Choosing terseness over readability” case 1: variable “p” should be “parent”
@Sergey Chikuyonok, yes, using
+=
oninnerHTML
is generally a bad idea but that wasn’t the point of the snippet – I was just demonstrating the effectiveness of joining HTML strings using a native method. And, frankly, on the inline-event-handlers issue, it’s each to his own – I for one don’t think the marginal performance benefits of inline handlers outweigh the benefit in having fully unobtrusive handlers.@Mark van ‘t Zet, so who exactly are you accommodating to in your comments? Any proficient JavaScript developer should understand the following construct:
It’s illogical to constantly assume that the reader of your code is just a beginner – you should assume they have a very good knowledge of the language.
Very useful and elucidative article. Thank you for share.
I especially agree with your Having long HTML strings in your JavaScript point. Not only does inserting HTML with JavaScript rob you of any SEO value for some search engines, but it’s a bad practice if a user would have JavaScript disabled.
Overall, this is a great list. Thanks
“Only functions that are constructors should be identified with an initial captial letter, not regular functions.”
Then what’s your quick visual distinction between a variable and a method, hmm?
The entirely different semantics of the name?
@Dave,
Lea is right. If you know and follow the Java variable/method naming convention, you can’t go wrong.
Just wanted to say that I found the format of this entry–here’s what NOT to do–very helpful as a javascript beginner. I commit, to one degree or another, ALL of these sins in my javascript, and it’s very useful for me to see the signs of the way I do things now with a follow-up pointer to an improved method. Thanks!
I agree with some but not all. I do not agree with the use of third party libraries, period. Inline JS is sometimes necessary so remember that.
Very nice article. By the way, can you tell us which book do you prefer to learn advanced JavaScript and things like in this article?
Thank you.
@John
‘JavaScript: The Good Parts’ by Douglas Crockford is great, but somewhat advanced.
http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742