Content:
When building a UI, it’s common to loop over elements and append them to your HTML.
for (i = 0; i < length; i++) {
parent.innerHTML += '<button id="button-' + i + '>' + text + '</button>';
}
When using buttons or link tags, you’re also likely to want to add an event listener, to handle the onclick
event.
for (i = 0; i < length; i++) {
parent.innerHTML += '<button id="button-' + i + '>' + text + '</button>';
const buttonElement = parent.querySelector('#button-' + i);
buttonElement.onclick = (() => {
...
});
}
This looks like it should work as intended. For each element, the parent innerHTML
is updated to add a new HTML button
element, and a new onclick
event listener is attached to the button.
Running the code, however, you’ll find that only the last element in the loop has an event listener attached.
This is a simple issue to fix, but only once you’re aware of the cause.
Altering the innerHTML
property of the parent element directly causes all event listeners currently attached to to be removed.
The fix it to alter the code to avoid altering the innerHTML
property of the parent element.
The easiest way to do this is to use the insertAdjacentHTML
function. This function allows HTML to be added to the element, in the position specified.
parent.insertHTML(position, html);
Valid values for position are as follows:
beforebegin
: Inserts content before the element opening tagafterbegin
: Inserts content after the element opening tag, but before existing content.beforeend
: Inserts content after the content currently inside the elementafterend
: Inserts content after the element closing tag
A visual representation of this is as follows:
<!-- beforebegin -->
<div class="parent-class">
<!-- afterbegin -->
<div class="existing-content">
<!-- beforeend -->
</div>
<!-- afterend -->
The direct replacement for +=
would be beforeend
.
Using insertHTML
retains the event listeners currently attached to the content within the parent.