Manipulating innerHTML removes events

Others have written about this before, but I thought I’d mention it again, just so you don’t miss it. Aleksandar Vaci? found it while playing with tables and their cells. I found it when Robert and I played with nested lists. It works the same across browers. Let me show a quick example:

You have a paragraph tag that contains a span that you want to make clickable.

This is clickable.
But this is not.

To make it clickable you don’t do any fancy stuff, you just add it with onclick:


var span = document.getElementsById("clickspan");
span.onclick = function() {
alert("You clicked the span!");
}

All fine. You click the span and it just works. But then you remember something. You want to add some text to the end of the paragraph, and you decide to do this with javascript. You add the following line to the end of the script:


var p = document.getElementById("para");
p.innerHTML += " Some extra text";

You try clicking the span again, and it doesn work. You scratch your hair, you bite your nails, you scream of desperation and anger. It still doesn’t work. It seems manipulating an element by using innerHTML removes all events from that element, and all children. Here’s a live example. I thought you should know.

18 responses to “Manipulating innerHTML removes events

  1. thats why you avoid using innerHTML :)

    Just kidding. Do you have the same results if you were to try appending via the DOM instead of innerHTML?

  2. @Nate: No, using DOM methods instead keeps the events as they should be. That’s one workaround. The other one is to attach events after using innerHTML.

  3. I’ll have to call ‘duh’ on that one. Obviously, events on an element cannot be retained if the element is replaced by a completely different string using innerHTML. You basically throw away that part of the DOM and write it new. Don’t have to tell you that.

    The only thing slightly noteworthy is that this also applies when using the + unary operator (“+=”). And given that it is required to call GetValue() and return the sum, which is in turn written back as a whole, it is not very surprising.

  4. @Freddy: Good that you found it obvious, I just had to think for a while before it came to me. I thought I might save someone’s time by stating it, but deeming by your tone I might have been wrong about that.

    As you said, the confusing part here is that adding to and replacing innerHTML is the same.

    Thanks for commenting.

  5. @Emil: I didn’t want to sound elitist, sorry if it came across as such. JFTR: In JavaScript, there is no “adding to” strings; Adding using the unary + (“+=”) is just a “shorthand” for reading, adding, and reassigning values (internally, they convert number/string types, but that’s not relevant in this context).

  6. Hmm, nothing happens when I click on any of those elements in Safari and Camino.
    Is this an IE only thing?

  7. Nice, I did not know this.

    Johan Lind:
    Look at the source of the example page, the JavaScript isn’t supposed to do anything when you click the span, because of the innerHTML change.

  8. Maybe i’m missing something but why add the onClick event using span.onclick=function()?, if you put the “onClick” inside the HTML tag it works fine even after the innerHTML change

  9. Yeah, I came across this problem when writing an Ajax driven playlist for a project here at work. I was adding the new contents to the DOM with innerHTML and had to rebind all of the events for the entire list every time. Using DOM methods though will avoid this problem.

  10. Freddy,

    Thanks for explaining that! It threw us off for a while, so we just needed to think in alternate terms. In this case it was an AJAX call that could return any kind of HTML code, so using proper DOM methods wasn’t a viable option.

  11. @Jose Carrero: Many believe (including me) that adding things inline in the code like that is littering. You attach behaviour into the structure of the document. JS files should add their events from an external file, with onclick or something fancier.

  12. @Anders Ringqvist: Very interesting technique, thanks for linking! If I understand it correctly it’s simply setting a listener on the outermost element, and waiting for the event to bubble there. Good way to avoid this problem.

  13. Pingback: AdvancED DOM Scripting » Out from under the carpet
  14. Another way around this problem is to create your own appendHTML() method like so:

    HTMLElement.prototype.appendHTML = function(s) {
    var div = document.createElement(‘div’);
    div.innerHTML = s;
    while (div.firstChild)
    this.appendChild(div.firstChild);
    }

    This does what you thought “innerHTML +=” did (except that script blocks inserted this way will be executed).

  15. Dear all,

    I try to use div instead of span on the above coding.
    The result is that the event of div is kept while span’s is “killed”.

    kachun

  16. Dear All,

    In conclusion, the innerHTML, DOM and html methods are used in following codes to show the event differences:

    events of innerHtml and DOM dynamic html

    function changeinnerhtml(_id)
    {
    var obj = document.getElementById(_id);
    obj.onclick = function(){alert(_id + ‘ is clicked!’)};
    var objparent = document.getElementById(‘para1’);
    objparent.innerHTML += ‘ YAHOO click me!‘;
    }
    function changedom(_id)
    {
    var obj = document.getElementById(_id);
    obj.onclick = function(){alert(_id + ‘ is clicked!’)};
    var objparent = document.getElementById(‘para1’);
    var objnew = document.createElement(‘a’);
    objnew.setAttribute(“href”,”http://google.com.hk”);
    objnew.appendChild(document.createTextNode(“GOOGLE click me!”));
    obj.appendChild(objnew);
    }

    Compare about event handlers of innerHTML, DOM and static html
    innerHTML here
    DOM here
    hard coded html here

    (1)When you click “innerHTML here”, no alert because parent tag (&lt p &gt) changed by innerHTML destroys the event.
    (2)When you click “DOM here”, alert prompts dom tree is built within parent tag (&lt p &gt).
    (3)When you click “hard coded html here”, alert should prompt because no structure changing within parent tag (&lt p &gt).

    changeinnerhtml(‘span1’);
    changedom(‘span2’);

    Regards,
    wongkachun

Comments are closed.