I like to hang around the code review stack exchange. One of the most common issues I see with beginner Javascript developers is their constant reliance on using .innerHTML
, without properly sanitizing the data. Sometimes more seasoned developers use it too, despite the risk, simply because it’s much simpler to use than alternatives. In some cases this can just lead to potential bugs, but in the worst scenarios, these developers introduce XSS issues into their code.
I can understand the desire to use .innerHTML
. The alternative is very unfriendly and cumbersome to use. Compare these two examples of creating a very small chunk of HTML (and imagine how much more verbose it gets as the amount of generated HTML grows, or how unreadable it gets when a little nesting gets involved)
Without .innerHTML:
const heading = document.createElement('a')
heading.classList.add('heading')
heading.href = './over-there.html'
const img = document.createElement('img')
img.classList.add('img')
img.src = './my-image.png'
img.alt = 'some alt text'
heading.appendChild(img)
const text = document.createElement('h1')
text.classList.add('heading-text')
text.innerText = 'Information'
heading.appendChild(text)
const body = document.createElement('p')
body.innerText = 'Lorum Ipsum'
heading.appendChild(body)
document.getElementById('main').appendChild(heading)
With .innerHTML
document.getElementById('main').innerHTML = `
<a class="heading" href="./over-there.html">
<img class="img" src="./my-image.png" alt="some alt text"/>
<h1 class="heading-text">Information</h1>
<p>Lorum Ipsum</p>
</a>
`
What if we provided a more ergonomic create-element function that tried to compete with how user-friendly .innerHTML
is? This could help drive people away from relying on .innerHTML
so much, and guide them to better and safer alternatives. For example, we could provide an Element.create()
function that was defined somewhat like this:
Element.create = function(tagName, attrs = {}, children = []) {
const newElement = document.createElement(tagName)
for ([key, value] of Object.entries(attrs)) {
newElement.setAttribute(key, value)
}
newElement.append(...children)
return newElement
}
And here’s a usage example
const heading = Element.create('a', { class: 'heading', href: './over-there.html' }, [
Element.create('img', { class: 'img', src: './my-image.png', alt: 'some alt text' }),
Element.create('h1', { class: 'heading-text' }, ['Information']),
Element.create('p', {}, ['Lorum Ipsum']),
])
document.getElementById('main').appendChild(heading)
Yes, this isn’t as concise as .innerHTML
, but it’s certainly orders of magnitude better than the current DOM API functions we’ve got, and will hopefully be enticing enough to lead people away from .innerHTML
.
And yes, I acknowledge that it’s not that hard to just define this Element.create() in userland code, but newer Javascript programmers aren’t going to think to do that.