Using 'innerHTML' in JavaScript
Tags: html, javascript, webdev, beginners
Published: 2023-02-05 18:11:21

---
description: Towards the beginning of my journey learning web development, I found out that using `.innerHTML` in your JavaScript code is not usually the best idea - for security, and other reasons. Read more to learn more!
---

When working on a project recently, I stumbled on an issue where things weren't working as expected and I didn't understand why. As developers, we've all been there. Fortunately, with this particular issue I got lucky and it didn't take me very long to figure out what the problem was. I decided to share the details here to hopefully help somebody else facing similar issues!

The problem had to do with me using JavaScript to access and change an HTML elements' `innerHTML`, or more specifically, using `+=` to update the contents of the `innerHTML` within a `forEach` block. 

I had no idea that using `+=` to add content to an elements' `innerHTML` could possibly cause issues, and as it turns out after doing some research, I didn't really understand that much about using the `innerHTML` property in general. Let's go over the basics of what `innerHTML` is now by comparing it to two other properties, `innerText` and `textContent`.

This example comes directly from W3 Schools website on their page about innerHTML, but I think it perfectly demonstrates each property so I'm going to recreate it here. To see the full example plus other information, you can visit [W3Schools](https://www.w3schools.com/jsref/prop_html_innerhtml.asp).

HTML:
```
<p id="myP"> This element has extra spacing    and contains <span>a span element</span>.</p>
```
JavaScript:
```
let text = document.getElementById("myP").innerText;
```
This example with `innerText` will return:
```This element has extra spacing and contains a span element.```

```
let text = document.getElementById("myP").textContent;
```
This example with `textContent` will return:
```This element has extra spacing    and contains a span element.```

```
let text = document.getElementById("myP").innerHTML;
```
This example with `innerHTML` will return:
```This element has extra spacing    and contains <span>a span element</span>.```


As you can see, an elements' `innerHTML` includes HTML tags for any children of the element, as well as any text that it or its children contains. This means that, when adding content to an elements' `innerHTML`, it is possible to include tags to create child elements, although it's recommended to use `appendChild` to add child elements rather than using `innerHTML`. 

With `innerHTML`,  it's even possible to include tags for a script, although W3Schools states than any script tags added to an element using their `innerHTML` will not be executed. Unfortunately, it's possible for bad characters to get around this using, for example, an `img` tag: they can make it so that their image will never load and provide an `onerror` attribute that can then include malicious code that will be executed by the browser when it inevitably produces an error when trying to load the image! This makes using an elements' `innerHTML` a security risk, so it is not advisable to add any user-generated content to your site by amending an elements' `innerHTML` property. For example, in a comment system, users would be able to write code to hack other users' private information if the developer were to add new user comments to their websites' by adding the comment contents to an elements' `innerHTML`! This is when using `textContent` or `innerText` is a better option. If you must use `innerHTML` for some reason, you should sanitize the content before using it. MDN describes a handy function to use for exactly this purpose: [`SetHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/setHTML)

If you need to keep HTML markup that would be sanitized by the `SetHTML` function but still want the security of knowing there's no harmful content, there is also a helper library from [DOMPurify](https://github.com/cure53/DOMPurify) that will remove any HTML markup that is not whitelisted. 

###So, what about using += with `innerHTML`?
As concerning as the security risk with using `innerHTML` is, it's time to get back to the reason I started researching `innerHTML`, and the reason for this post. 

When appending to an elements' `innerHTML`, the whole element is reparsed. To show what this means, let's look at my code that was not working as expected:

```
...
const td = document.querySelector('#evo_into');
chain.forEach(el => {
  td.innerHTML += ', <a id="' + el.species['name'] + 
  '"href="user_search_bar">' + el.species['name'] + '</a>';
 const element = document.querySelector(`#${el.species['name']}`);
  element.addEventListener('click', function() {...})
}
```
In this code, I was trying to add a click event listener for every item within the chain of evolutions (yes, it's a Pokémon project!). This is specifically for cases where one Pokémon can evolve into multiple different Pokémon. So, for each evolution, I was adding an `a` element with the text being equal to the name of the Pokémon, and the link just bringing the user back to the top of the page. I then attempted to add an event listener on each evolutions element that would load more information about that specific Pokémon to the DOM when the link is clicked. However, I was confused to find that none of the event listeners worked except the very last one! Why might this be? Well, because appending to the innerHTML reparses the entire content of the `innerHTML` string, each time the code in the `forEach()` block was run and I used `+=` to append to the `innerHTML`, all the elements that I had created before that were essentially recreated, except, of course, the code to create their event listener was not re-executed. This is why only the last element in the evolution list had a working click event listener; there were no more additions to the `innerHTML`, and the element was not reparsed again, leaving one intact event listener. 

To solve this, I simply created a string that I added to with each cycle of the `forEach()` block, and after that block I added the entire string to the innerHTML and created the event listeners after that. This is of course just one way I could have solved this problem; another solution I might have gone with would be to make use of a handy function, [`insertAdjacentHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML) that would allow me to specify where in the element I wanted to insert my new HTML content, preserving the previous content as-is and therefore keeping any existing event listeners active. 

As developers, we often come across errors or unexpected behavior in our code that can be frustrating to say the least! But it's important to remember that these mistakes are an opportunity to learn and understand more about our code. Without this experience, I may never have done research into using `innerHTML`, and in the future I might have misused it in a way that was dangerous to any users of my websites without realizing it! My mistake helped me grow as a developer, and I hope the experience can help other new developers out there as well. Thanks for reading!