Monday, January 16, 2012

jQuery's .html() method, and the Undocumented Argument Type

By Dave Leeds

So the other day I was sending a JavaScript string into jQuery's .html() method, and I got this funky, funky little exception in Firefox:

Error: uncaught exception: [Exception... "Could not convert JavaScript argument arg 0 [nsIDOMDocumentFragment.appendChild]" nsresult: "0x80570009 (NS_ERROR_XPC_BAD_CONVERT_JS)"
And in Internet Explorer, the message was this:
SCRIPT5022: DOM Exception: HIERARCHY_REQUEST_ERR (3)

Now, according to the officially official jQuery API specs for the html method, when you call .html() as a setter, you can pass in one of two types of arguments -- either a string of HTML, or a function that returns the HTML to set.

But in fact, there's one other type of input accepted that isn't (as of today, anyway) mentioned in the documentation -- a jQuery object:

Perfectly legit, that chunk o' code there. In the case above, #target's contents will be replaced with every span element on the page. In other words, those spans will all move from wherever they are in the DOM, into the #target element.

Now what happens when there's more than one target? For example, on a page with two divs and two spans, what will the following code do?

Don't worry -- I won't leave you hangin'. Never did much like those "exercise for the reader" things anyway. The two spans will vanish from their present location, and a copy is placed into each div.

So, back to my original problem -- why was my string input causing the exceptions above? Because I had sent in a String object, rather than a String primitive.

jQuery evaluates the argument you send to .html() using typeof. If it evaluates to "string", it'll get handled like a string. So, is a JavaScript string a typeof "string" or "object"?

Interestingly, that evaluates to "object".

This, however, evaluates to "string":

This is the nuance that causes jQuery to branch in the wrong direction -- if you pass .html() something that's not a typeof string, and it isn't a function, then jQuery makes the assumption that it's a jQuery object. And when it tries to append the object as a node onto the DOM (using node.appendChild), an exception is thrown, because it's not the right kind of object.

Incidentally, if you're stuck with a JavaScript string object rather than a string primitive, you can morph it into a primitive with the .valueOf() method.

No comments:

Post a Comment

Profile Picture
Dave Leeds
My Hobbies:
  • Programming
  • Cartooning
  • Music Writing
Full Profile