views:

349

answers:

11

What are some of the common Javascript and/or jQuery "gotchas" that you have seen or been tripped up by?

As an example, it was pointed out to me this morning that you have to be careful with the radix when doing parseInt(), because if your string starts with a 0, the radix will default to 8.

+3  A: 

i would recomend to read entire http://wtfjs.com/

vittore
Nice site. It explains one of my questions btw. :P
BrunoLM
first question is about MY site and blog! http://www.typeofnan.com :p
jAndy
A: 

Javascript

parseInt('0aaa') //still returns 0
parseFloat('0.aaa') //still returns 0

This ones more of an oops factor. When writing a switch statement, I have been known to forget to place a break in the statement, which causes issues.

var myvar = '1'
switch (myvar) {
  case '1':
     alert(myvar);
  case '2':
     alert(myvar);
     break;
}

Because I forgot the break in the case '1' you will receive the alert twice.

John Hartsock
the switch is not a JS/JQUERY 'gotchas'. This is also possible in other languages C/C++/C# etc.
PoweRoy
@PoweRoy...I know...that is exactly why I said its "more of an oops factor"
John Hartsock
@PoweRoy: Nitpicker's corner - it is a [compile-time error](http://msdn.microsoft.com/en-us/library/06tc147t.aspx) for control to fall through a switch case label in C#. In other words, John's code wouldn't compile if it were C#.
josh3736
+7  A: 

Replacing .round() / .floor() or .ceil() with ~~ to cut away decimal places is pretty handsome.

Instead of

Math.floor(Math.random() * 100);

you do

~~(Math.random() * 100);

It's just shorter and has a better performance even!

jAndy
That's more of a tip/trick than a gotcha, but still very cool!
Ender
I could see `.floor()` and `.ceil()` if you add 1, but it doesn't seem like much of a replacement for `.round()`. Maybe that's the *gotcha* part? ;o)
patrick dw
@patrick: well in the context just to get rid of decimal places, it indeed is.
jAndy
other bitwise methods. 0|(Math.random() * 100); (Math.random() * 100)>>0;
Kenneth J
Readability counts for something too. You may think it is clever but you may just confuse the hell out of whoever may have to maintain the code in the future
Kenneth J
+2  A: 

Douglas Crockford's book JavaScript: The Good Parts is an excellent read about the topic. There are also the bad and awful parts of Javascript in it :)

Epeli
+8  A: 

The scope of 'this'

Consider the following example:

function myClass(message) {
  this.message = message;
  $('button').click(function(){
    this.onClick();
  });      
}

myClass.prototype.onClick = function() {
  alert(this.message);
}

Of course, line 3 does not work: this works on a per-function basis, and the function defined on line 2 will have the selected button as this instead of the instance of myClass. There's an even sneakier version:

function myClass(message) {
  this.message = message;
  $('button').click(this.onClick);    
}

myClass.prototype.onClick = function() {
  alert(this.message);
}

This calls the correct function, but this would still be the button (because this is determined by the caller, not by the fact that you're a member function). The correct solution is to use an intermediate variable, such as self, to apply sane scoping rules to this:

function myClass(message) {
  this.message = message;
  var self = this;
  $('button').click(function() { self.onClick(); });    
}

Type casting

[] == ![] evaluates to true for obscure reasons

Arrays and Properties

The length property only works for integer keys on arrays. For instance:

var a = [];        // a.length == 0
a[0]   = 'first';  // a.length == 1
a['1'] = 'second'; // a.length == 2
a['x'] = 'third';  // a.length == 2 <-- 'x' does not count

While on this topic, for( in ) works on arrays, but traverses all properties defined on the array. This means that your JavaScript code that uses for( in ) to iterate through arrays will suddenly stop working on certain browsers when you add ExtJS to your project, because ExtJS defines Array.prototype.filter if it's not present (this is known as monkey patching). Since now filter is a custom property of every array object, all your loops also iterate through it. Boom.

The clean way of iterating through an array is using length and an incrementing for loop. Or jQuery's $.each().

Victor Nicollet
+8  A: 

The biggest WTF that I have come across in Javascript must be String.replace.

Take this code for example:

"An,example,of,a,comma,separated,sentence".replace(',', ' ');

This produces a sentence nicely separated by spaces instead, right?

To my utter surprise: no, it does not. It only replaces the first comma with a space, leaving the rest untouched. If you want to do an actual replace, you have to use .replace(/,/g, ' ').

Why the method is not called 'replaceFirst' is anyone's guess.

JulianR
indeed, a common misconception (*actually deviation from the norm*)
Gaby
The trick is that according to the original Netscape Docs that I have, the signature for this method is `String.replace(regexp, newText)` In many cases a simple "String" is used as the first parameter thus the concept of the global flag for the regexp is completely overlooked.
scunliffe
Almost every string replace routine I've ever seen has had a "just the first one" mode as the default. As an example, the "vi" replace command, and all the various Unix/Linux variants of that like "sed", require a "g" suffix to do a "replace all". The Java library has "replace" and "replaceAll", with the same semantics (well as far as the replacement policy anyway).
Pointy
+1  A: 

Variable scope and loop variables is definitely a "gotcha". This happens a lot when assigning event handlers in a loop:

var lis= document.getElementsByTagName('li'),
    imgs = document.getElementsByTagName('img');
for (i = 0; i < elements.length; i++)
{
    elements[i].onclick = function() {
        imgs[i].src = elements[i].innerHTML;
    }
}

At the time of execution, imgs[i] will be undefined, throwing an error when trying to access imgs[i].src. The same for elements[i] and its innerHTML.

Ryan Kinal
Very true, I answered a question yesterday where this was the cause of the problem.
Ender
A: 

Implied globals are a big one:

var x = 12, y = 14;
function addOne(num)
{
    x = num + 1;    // since this is not declared with 'var', it is implied global
    return x;
}

alert(addOne(y));    // alerts '15'
alert(addOne(x));      // alerts '16'

It's a simple, and unrealistic example, but demonstrates the problem nicely.

Ryan Kinal
+1  A: 

A very good article was recently by Elijah Manor on his blog, Enterprise jQuery. It's written on Javascript "gotchas" for C# developers. It's a part series and there are a number of good points that will also "get" individuals familiar with other languages.

Check it out here: How Good C# Habits can Encourage Bad JavaScript Habits.

JasCav
Thanks for the mention. Glad you enjoyed the article. Post #2 came out today and a 3rd post should be out next week.
Elijah Manor
@Elijah - No problem, and thank you for a great series of articles. As a C# developer who has been doing my fair share of JavaScript recently, it was helpful (particularly the false-y stuff).
JasCav
+5  A: 

The return statement must have a value immediately following it, or at least starting on the same line:

return 4;   // returns 4
return {};  // returns an empty object
return {    // returns an empty object
};          
return      // returns undefined
{
};
Ryan Kinal
+2  A: 

Ajax gets/posts to a static URI can get cached by IE. So you need to append an ever changing parameter, a quick and easy way is to append new Date().getTime()

Flash84x
Or you can define: $.ajaxSetup({cache: false}); which will add the date/time on the end of any Ajax gets / posts.
Thqr