Tutorial :Is it possible to append to innerHTML without destroying descendants' event listeners?



Question:

In the following example code, I attach an onclick event handler to the span containing the text "foo". The handler is an anonymous function that pops up an alert().

However, if I assign to the parent node's innerHTML, this onclick event handler gets destroyed - clicking "foo" fails to pop up the alert box.

Is this fixable?

<html>   <head>   <script type="text/javascript">      function start () {      myspan = document.getElementById("myspan");      myspan.onclick = function() { alert ("hi"); };        mydiv = document.getElementById("mydiv");      mydiv.innerHTML += "bar";    }     </script>   </head>     <body onload="start()">     <div id="mydiv" style="border: solid red 2px">       <span id="myspan">foo</span>     </div>   </body>    </html>  


Solution:1

Unfortunately, assignment to innerHTML causes the destruction of all child elements, even if you're trying to append. If you want to preserve child nodes (and their event handlers), you'll need to use DOM functions:

function start() {      var myspan = document.getElementById("myspan");      myspan.onclick = function() { alert ("hi"); };        var mydiv = document.getElementById("mydiv");      mydiv.appendChild(document.createTextNode("bar"));  }  

Edit: Bob's solution, from the comments. Post your answer, Bob! Get credit for it. :-)

function start() {      var myspan = document.getElementById("myspan");      myspan.onclick = function() { alert ("hi"); };        var mydiv = document.getElementById("mydiv");      var newcontent = document.createElement('div');      newcontent.innerHTML = "bar";        while (newcontent.firstChild) {          mydiv.appendChild(newcontent.firstChild);      }  }  


Solution:2

Using .insertAdjacentHTML() preserves event listeners, and is supported by all major browsers. It's a simple one-line replacement for .innerHTML.

var html_to_insert = "<p>New paragraph</p>";    // with .innerHTML, destroys event listeners  document.getElementById('mydiv').innerHTML += html_to_insert;    // with .insertAdjacentHTML, preserves event listeners  document.getElementById('mydiv').insertAdjacentHTML('beforeend', html_to_insert);  

The 'beforeend' argument specifies where in the element to insert the HTML content. Options are 'beforebegin', 'afterbegin', 'beforeend', and 'afterend'. Their corresponding locations are:

<!-- beforebegin -->  <div id="mydiv">    <!-- afterbegin -->    <p>Existing content in #mydiv</p>    <!-- beforeend -->  </div>  <!-- afterend -->  


Solution:3

Now, it is 2012, and jQuery has append and prepend functions that do exactly this, add content without effecting current content. Very useful.


Solution:4

As a slight (but related) asside, if you use a javascript library such as jquery (v1.3) to do your dom manipulation you can make use of live events whereby you set up a handler like:

 $("#myspan").live("click", function(){    alert('hi');  });  

and it will be applied to that selector at all times during any kind of jquery manipulation. For live events see: docs.jquery.com/events/live for jquery manipulation see: docs.jquery.com/manipulation


Solution:5

I created my markup to insert as a string since it's less code and easier to read than working with the fancy dom stuff.

Then I made it innerHTML of a temporary element just so I could take the one and only child of that element and attach to the body.

var html = '<div>';  html += 'Hello div!';  html += '</div>';    var tempElement = document.createElement('div');  tempElement.innerHTML = html;  document.getElementsByTagName('body')[0].appendChild(tempElement.firstChild);  


Solution:6

Losing event handlers is, IMO, a bug in the way Javascript handles the DOM. To avoid this behavior, you can add the following:

function start () {    myspan = document.getElementById("myspan");    myspan.onclick = function() { alert ("hi"); };      mydiv = document.getElementById("mydiv");    clickHandler = mydiv.onclick;  // add    mydiv.innerHTML += "bar";    mydiv.onclick = clickHandler;  // add  }  


Solution:7

You could do it like this:

var anchors = document.getElementsByTagName('a');   var index_a = 0;  var uls = document.getElementsByTagName('UL');   window.onload=function()          {alert(anchors.length);};  for(var i=0 ; i<uls.length;  i++)  {      lis = uls[i].getElementsByTagName('LI');      for(var j=0 ;j<lis.length;j++)      {          var first = lis[j].innerHTML;           string = "<img src=\"http://g.etfv.co/" +  anchors[index_a++] +               "\"  width=\"32\"           height=\"32\" />   " + first;          lis[j].innerHTML = string;      }  }  


Solution:8

I'm a lazy programmer. I don't use DOM because it seems like extra typing. To me, the less code the better. Here's how I would add "bar" without replacing "foo":

function start(){  var innermyspan = document.getElementById("myspan").innerHTML;  document.getElementById("myspan").innerHTML=innermyspan+"bar";  }  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »