If you want to create dynamic application in Java Script without frameworks like React or Vue.JS (and sometimes even with these), you will face the problem: how to create HTML elements dynamically in JavaScript.
First approach – bad
First approach you can do is to create string with HTML code. Then you can put it to innerHTML property.
let elements = '<p class="sample-class">First paragraph</p>';
document.querySelector('.container').innerHTML = elements;
//or with JQuery
$('.container').html(elements);
But this approach has two problems. First: if you want to generate content more dynamically (using ifs or loops) your code will be very ugly:
let elements = '<ul>';
for(let i=0;i<10;i++){
elements+=`<li class="${i%2==0:'even':'odd'}">Element</li>`;
}
elements+='</ul>';
Second problem: if you want to use any variable inside, you need proper escaping, so content of this variables won’t be treated as HTML. Otherwise:
let title = "a+b<c";
let elements = `<h1>${title}</h1>`;
console.log(elements)// => "<h1>a+b<c</h1>"
Omitting escaping can lead also to security problems (XSS)
Second approach – good
Second approach is to use proper DOM’s methods to create elements.
let element = document.createElement('p');
element.classList.add('sample-class');
element.textContent = 'First paragraph';
document.querySelector('.container').append(element);
You don’t need to worry about XSS.
let title = "a+b<c";
let element = document.createElement('h1');
element.textContent=title;
console.log(element.outerHTML)// => "<h1>a+b<c</h1>"
But problem is, that in this approach code starts to be long and writing starts to be tiring
My approach
To make coding easier I created library fast-creator (github, npm). Main idea is to put all details about new HTML element in one object and let the library to do boring stuff.
import {create} from 'fast-creator';
let element = create({tagName:'p', className:'sample-class', text:'First paragraph'});
document.querySelector('.container').append(element);
Code is shorter, and also you don’t have any problems with escaping strings if you want to use variables.
Let’s see bigger example. Code and equivalent HTML:
import {create} from 'fast-creator';
create({
tagName: 'a',
href: '/',
title: 'some link',
data: {
a: 1,
b: 2
},
classList: ['link', 'sample'],
children: [{
tagName: 'span',
text: 'Link'
}]
})
<a href="/" title="some link" data-a="1" data-b="2" class="link sample"><span>Link</span></a>
Type of tag is provided by tagName (default is div). Content could be set in 3 ways: text (which is transferred to textContent), html (to innerHTML) or children (which is an array that is executed recursively).
All other properties of object are translated to attributes (like in example href and title). Exception of this rule is class, because it is keyword in ES6, so there are properties className (string) and classList (array of strings). Second exception is data – you can put there attributes like to .dataset property of HTML elements.
Emmet
There is a plugin to code editors called Emmet. It converts css selectors for HTML. It inspired me to make code even shorter:
import {create} from 'fast-creator';
create('section.first#main')
<section class="first" id="main"></section>
Of course this not always will be helpful, especially if you need to read data from external variables, so you can combine this to methods
import {create} from 'fast-creator';
create('article', {
className: 'main',
children: ['p.first', 'p.last']
})
<article class="main"><p class="first"></p><p class="last"></p></article>