Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

People also ask

Can you append to innerHTML?

To append using the innerHTML attribute, first select the element (div) where you want to append the code. Then, add the code enclosed as strings using the += operator on innerHTML.

Does innerHTML remove event listeners?

'innerHTML' Removes Event Listeners Since the entire tag (the div in our code) is being reparsed, the event listener is lost in the process. Rather than using the += operator, use DOM functions such as append() .

Can you += innerHTML?

innerHTML will always reproduce an identical copy of the element's DOM. You have nothing that shows what innerHTML references initially, with the += you will just be appending content onto existing content. Also, its better to add content to the DOM using the functions appendChild.

What is the disadvantage of innerHTML?

There is no append support without reparsing the whole innerHTML. This makes changing innerHTML directly very slow. innerHTML does not provide validation and therefore we can potentially insert valid and broken HTML in the document and break it.


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);
    }
}

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 -->

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


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


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);

Yes it is possible if you bind events using tag attribute onclick="sayHi()" directly in template similar like your <body onload="start()"> - this approach similar to frameworks angular/vue/react/etc. You can also use <template> to operate on 'dynamic' html like here. It is not strict unobtrusive js however it is acceptable for small projects

function start() {
  mydiv.innerHTML += "bar";
}

function sayHi() {
  alert("hi");
}
<body onload="start()">
  <div id="mydiv" style="border: solid red 2px">
    <span id="myspan" onclick="sayHi()">foo</span>
  </div>
</body>