views:

185

answers:

3

I've been running my head into a wall trying to figure this out. Take the following HTML body:

<body>
<div id="project">
  <h1>Hi</h1>
  <h2>Hello</h2>
</div>
</body>

And the following jQuery code:

$(function(){
  var h = $('#project').html();
  $('#project').remove();
  $(h).hide().appendTo('body');
  alert("Created HTML, hide, and appended!");
});

The $(h).hide() portion causes jQuery to throw an exception in Safari 4 and Firefox 3.5.

Safari: TypeError: Result of expression 'this[a].style' [undefined] is not an object.
Firefox: uncaught exception: [Exception... "Could not convert JavaScript argument arg 0" nsresult: ...]

When I change the HTML to contain just one of the two headings (if you remove the <h1> or <h2> from the HTML, the script runs successfully. Why is this?

To try for yourself, see http://jsbin.com/avisi/edit

Edit: I'm not actually trying to remove and element from the DOM and re-insert it by copying the HTML. This is just a test case for an error I'm having in more complex code, and I'm trying to understand why this error occurs. I agree that, if I wanted to accomplish just what is shown here, I would use something like $('#project').remove().children().appendTo('body')

A: 

You removed the contents from the DOM before, so there is nothing to hide. If you would do

$(h).appendTo('body').hide();

it should work

harpax
There is something to hide. Just because the node has been removed from the document doesn't mean that it doesn't exist and that it can't have style manipulations performed on it.
Justin Johnson
you are correct, though I don't quite understand what raises the error then..
harpax
See my updated answer.
Justin Johnson
+7  A: 

I cannot duplicate your error in Firefox. However, you might want to try cleaning it up with the following:

$('#project').remove().children().appendTo('body').hide();

Broken down, this is what's happening

// Get the `project` container
$('#project')
    // Remove it from the page
    .remove()
    // Get its children (the h1, h2, etc)
    .children()
    // Append those nodes to the body
    .appendTo('body')
    // Hide those nodes
    .hide();

Others are proposing that .hide() is causing problems since the node that it is being applied to is not part of the main document; however, this is just not the case. As long as you maintain a reference to any node, you can affect its style property (via hide, show, etc).

One things you might want to check is to make sure that $('#project') is actually returning the (if any) expected node. Problems may arise otherwise.


So I poked around in Safari and found your problem. Here's a dump from the developer console.

> var h = $('#project').html();
undefined
> var t = $(h);
undefined

So far, so good. undefined here simply means that the statement (the var statement) has no return value (which it doesn't)

> t.hide()
ajax.googleapis.com/ajax/libs/jquery/1.4.0/jquery.min.js:131TypeError: Result of expression 'this[a].style' [undefined] is not an object.

Here's the error that you described. Inspecting each item in jQuery object will reveal the error below

> t[0]
<h1 style=​"display:​ none;​ ">​Hi​</h1>

Good...

> t[1]
(whitespace)

Dammit. Really? Here's the problem. whitespace nodes have no style attribute, which is what's causing the problem.

> t[2]
<h2>​Hello​</h2>

This is why copying the HTML of one node to another just to move those nodes is a bad technique. I suggest you use the snippet that I provided above.

Justin Johnson
I agree that this is a much cleaner way to remove and re-insert the nodes in question. What I'm trying to understand is why (at least in my copy of Firefox 3.5 and Safari 4), this above code causes an error.
Ryan
I found the cause and have updated my answer.
Justin Johnson
Justin, thanks for investigating this. I'm sure I can use this to solve my initial problem, which was trying to hide and insert an HTML fragment received from the server (which is why `.remove()` ... `.append()` isn't an option--there's nothing to remove).
Ryan
+1  A: 

There's a text node being selected in the $(h). We can filter that out using the filter function though.

This should work (I've only tested in FF though):

$(function(){
  var h = $('#project').html();  
  $('#project').remove();
  $(h).filter("*").hide().appendTo('body');

 alert("Created HTML, hide, and appended!");
});

Pretty wierd behaviour IMO.

Mark Worth
It's unfortunate but expected behavior. If you create DOM nodes from an HTML string, whitespace between the nodes is converted into text nodes. This often causes problems, which is why the technique is sometimes best to avoid, especially for the purposes of the OP.
Justin Johnson