views:

48

answers:

3

I've noticed a funny behavior and was wondering if anybody could shed some light on the reason.

It breaks down like this:

  • I've added a div and a button using Javascript's `appendChild'.
  • I add an onclick handler to the button, works fine
  • I add-assign the innerHTML of the div to add some more content
  • Button onclick stops working altogether

Here's a sample script:

var D = document.createElement("DIV"), B = document.createElement("BUTTON");
B.innerHTML = "Is it true?";
document.body.appendChild(D);
D.appendChild(B);

B.onclick = function() { alert("Elvis Lives!"); }

At this point, it works fine. Then, add this line:...

D.innerHTML += "What about Tupac?"; 

...and the button breaks. So, I'm just using appendChild for all elements now.

But -

  • Why is this happening ("seems like it should work :) ")
  • Is there a fix (besides appending all my extra elements)?
+1  A: 

Not an answer, just a test page - looks very strange - I can confirm your findings

<script>
window.onload=function() {
  var D = document.createElement("DIV"), B = document.createElement("BUTTON");
  D.id = "div1"
  B.innerHTML = "Is it true?";
  B.onclick = function() { alert("Elvis Lives!"); }
  D.appendChild(B);
  document.body.appendChild(D);
}
</script>
<a href="" onclick="document.getElementById('div1').innerHTML += 'What about Tupac?'; return false">Click</a><br>
<a href="" onclick="alert(document.getElementsByTagName('button')[0].onclick); return false">Test</a>

update: Lessons learned - be consistent.

This works as expected

<script>
window.onload=function() {
  var D = document.createElement("DIV");
  D.id = "div1"
  D.innerHTML = '<button onclick="alert(\'Elvis Lives!\')">Elvis</button>'
  document.body.appendChild(D);
}
</script>
<a href="" onclick="document.getElementById('div1').innerHTML += 'What about Tupac?'; return false">Click</a><br>
mplungjan
I can't lower myself to even glimpse a strange looking page! What is this schlock? :) Just kidding. Thanks for the confirm...
Steve
+4  A: 

(this is a lot of educated guessing) I think when you modify D's innerHTML, the dom within D is destroyed and created from the new value. So when the new contents are constructed, you are getting a brand new button, which is not the same node as B. since you set the onclick handler via js and not by an attribute on the original element, the new button does not have that function reference. however B still works as can be demonstrated by calling

B.click();

after you append to innerHTML.

to prevent the thrashing of the dom within D, you can do this instead of using innerHTML:

D.appendChild(document.createTextNode("What about Tupac?"));
lincolnk
Sounds good to me but I may await a few other opinions. Right, I'm using `appendChild` all over the place but `innerHTML` is just a few lines easier...
Steve
No need to wait. lincolnk's answer is the correct one.
ajax81
+2  A: 

innerHTML is a shortcut for creating DOM elements. When you append something to the outer div using innerHTML, this is what happens:

D.innerHTML += "What about Tupac?";

which is the same as,

D.innerHTML = D.innerHTML + "What about Tupac?";

which is the same as,

D.innerHTML = "<button>Is it true?</button>" + "What about Tupac?";

which finally becomes this,

D.innerHTML = "<button>Is it true?</button>What about Tupac?";

Now in the last step, we completely replaced the existing contents of the div, with a new string which contains HTML. As far as the DOM is concerned, it doesn't care whether a user typed the HTML by hand, or it came from calling innerHTML on a DOM node. All it cares about is, is that it has a string of HTML, which must be converted to a DOM. A new button element is created at this time, which is why the onclick stops working - it's not the same element anymore.

Anurag
BAM on the head. Thanks.
Steve