DEV Community

Cover image for Why You Should Stop Using `innerHTML +=` and Switch to `insertAdjacentHTML()`
Imoh Imohowo
Imoh Imohowo

Posted on

Why You Should Stop Using `innerHTML +=` and Switch to `insertAdjacentHTML()`

If you’ve ever dynamically added HTML in JavaScript, you’ve probably used innerHTML like this:

document.getElementById("container").innerHTML += "<div>New item</div>";
Enter fullscreen mode Exit fullscreen mode

But did you know this approach has major downsides?

  • It destroys existing event listeners
  • Resets form inputs and dynamic states
  • Hurts performance with large DOM updates

Luckily, there’s a better way: insertAdjacentHTML()!


What’s Wrong with innerHTML +=?

When you use innerHTML +=, the browser:

  1. Serializes the entire container’s HTML into a string
  2. Appends your new HTML
  3. Re-parses everything and recreates the DOM

This means:

  • All existing event listeners are wiped out
  • JavaScript references to elements break
  • Form inputs lose their values

Example of the Problem

// Add a button with a click listener
const container = document.getElementById("container");
container.innerHTML = `<button id="btn">Click Me</button>`;
document.getElementById("btn").addEventListener("click", () => alert("Clicked!"));

// Later, append more HTML (this BREAKS the listener!)
container.innerHTML += "<div>New content</div>";
// Now, the button’s click handler is GONE!
Enter fullscreen mode Exit fullscreen mode

insertAdjacentHTML() to the Rescue!

This method lets you insert HTML at specific positions without destroying the existing DOM.

Syntax

element.insertAdjacentHTML(position, htmlString);
Enter fullscreen mode Exit fullscreen mode

Four Insertion Positions

Position Description
'beforebegin' Before the element (as a sibling)
'afterbegin' Inside the element, before its first child
'beforeend' Inside the element, after its last child (most common)
'afterend' After the element (as a sibling)

Why It’s Better

Preserves event listeners (no DOM rebuild)

Faster performance (no full re-parse)

More control over placement


Practical Example: Dynamic List Updates

❌ Bad Way (innerHTML +=)

const list = document.getElementById("todo-list");
list.innerHTML += `<li>New Task</li>`; // Wipes existing listeners!
Enter fullscreen mode Exit fullscreen mode

✅ Better Way (insertAdjacentHTML)

const list = document.getElementById("todo-list");
list.insertAdjacentHTML("beforeend", `<li>New Task</li>`); // Safe!
Enter fullscreen mode Exit fullscreen mode

✅ Best Way (With Event Delegation)

// Insert safely
list.insertAdjacentHTML("beforeend", `<li class="task">New Task</li>`);

// Handle clicks via delegation (works even for new items)
document.addEventListener("click", (e) => {
  if (e.target.classList.contains("task")) {
    e.target.classList.toggle("completed");
  }
});
Enter fullscreen mode Exit fullscreen mode

When Should You Use It?

Appending dynamic content (chat messages, todos, etc.)

Avoiding DOM reset side effects

Optimizing frequent DOM updates

When Should You Avoid It?

If you need to replace an entire element (use innerHTML or replaceWith())

If inserting unsanitized HTML (risk of XSS—always sanitize first!)


Performance Comparison

insertAdjacentHTML() is much faster than innerHTML += because it doesn’t:

  • Re-serialize the entire container
  • Destroy and recreate DOM nodes

Benchmark Example

// Slow: innerHTML +=
for (let i = 0; i < 1000; i++) {
  container.innerHTML += `<div>Item ${i}</div>`;
}

// Fast: insertAdjacentHTML
for (let i = 0; i < 1000; i++) {
  container.insertAdjacentHTML("beforeend", `<div>Item ${i}</div>`);
}
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

  • Stop using innerHTML +=—it’s destructive and slow.
  • Use insertAdjacentHTML('beforeend', ...) for safe, efficient DOM updates.
  • Combine with event delegation for bulletproof dynamic elements.

Try It Yourself!

Next time you dynamically add HTML, replace innerHTML += with insertAdjacentHTML() and see the difference!

Top comments (0)