Tuesday, February 14, 2006

Safari quirk: Copying innerHTML

Here's a fun one. Given the following HTML, let's attempt to copy the contents of div1 to div2, then grab a reference to the SPAN element "foo."

<div id="div1">
  <span id="foo">Foo span</span>
</div>

<div id="div2"></div>

The easy way out is to just copy innerHTML from one element to the other, then destroy the contents of the original element:

document.getElementById('div2').innerHTML = 
    document.getElementById('div1').innerHTML;
document.getElementById('div1').innerHTML = '';
alert(document.getElementById('foo'));

In most browsers, the alert dialog displays "Element SPAN" or something similar. But in Safari 1.3 and above, the dialog displays null. Why?

For a brief moment, when we copy the innerHTML, we create two SPANs with identical IDs of "foo." Even after we destroy the contents of div1 (leaving only the one SPAN), the DOM is screwed up enough to confuse Safari, which continues to return null even after the DOM is "fixed." By contrast, Firefox for OS X is far more forgiving.

The solution seems to be: duh, don't copy from element to element. Instead, hold the contents in a variable until it's safe to reinsert them into the page:

var tempHTML = document.getElementById('div1').innerHTML;
document.getElementById('div1').innerHTML = '';
document.getElementById('div2').innerHTML = tempHTML;
alert(document.getElementById('foo'));

The dialog should now display "Object SPAN" in Safari. Of course, astute JS developers would probably prefer to skip using innerHTML altogether

4 Comments:

At 3:20 PM, Blogger scottandrew said...

You'd probably want to remove the original foo first before appending the clone, or once again you'll have a DOM with two foos ;)

Also, in the example code we're swapping the entire node contents, not just SPAN foo, so innerHTML isn't a bad way to go unless you wanna mess with DocumentFragments.

 
At 12:58 PM, Blogger Martijn said...

innerHTML rulez!!

 
At 12:19 PM, Blogger Dan Doru said...

This tip saved my day ! Thank you so much !

 
At 1:28 PM, Blogger Haig said...

You shouldn't even need to clone the node. Eg, to move "node" from container1 to container2:

  container2.appendChild(node);

to move it back:

  container1.appendChild(node);

It should be as simple as that!

Haig

 

Post a Comment

<< Home