Master the DOM

It’s not as hard as you might think

Introduction

Many web developers think the DOM is really difficult (or slow) and you need a huge framework to tame it. Then they invest a lot of their time to learn the framework. A year or two passes, another framework becomes popular and you need to learn everything from scratch. Repeat this a couple more times and JavaScript fatigue is born. Not to mention a huge pile of dependencies.

What if I told you DOM is not that hard? Would you believe me? Wouldn’t it be great to master the browser without being dependent on some third party? Would you give DOM a chance? Let’s find out:

DOM is not that hard and especially not slow.

First I’ll show you the basics and then some tricks from under the hood of RE:DOM, my tiny view library which makes mastering the DOM bliss.

Creating elements

Let’s start by creating HTML elements. To create an element, you just write document.createElement(tagName) — that’s it:

const h1 = document.createElement('h1')
// <h1></h1>

Modifying text content

HTML elements can be a bit empty if they don’t have any content, so let’s use element.textContent property to add some text:

h1.textContent = 'Hello world!'
// <h1>Hello world!</h1>

Attributes

To define an attribute for an HTML element, you can use element.setAttribute(name, value):

h1.setAttribute('class', 'hello')
// <h1 class="hello">Hello world!</h1>

To manage classes there’s the element.className property:

h1.className = 'hello'
// <h1 class="hello">Hello world!</h1>

However, the best way is to use classList:

h1.classList.add('hello')
// <h1 class="hello">Hello world!</h1>
h1.classList.remove('hello')
// <h1>Hello world!</h1>

To set an element’s ID, you can either use an attribute or the id property:

h1.setAttribute('id', 'hello-world')
h1.id = 'hello-world'
// <h1 id="hello-world" class="hello">Hello world!</h1>
If you’re uncertain whether to use attributes or properties, just use attributes – except with form elements’ states, like value and checked.

Note that with booleans you can’t use element.setAttribute(someBoolean, false), but some of these:

input.checked = true
// <input checked>
input.checked = false
// <input>
input.setAttribute(‘checked’, ‘’)
// <input checked>
input.removeAttribute('checked')
// <input>

Attach elements

HTML is structured. How do we attach elements to each other? Well, there’s parent.appendChild(child):

document.body.appendChild(h1)
// <body><h1>Hello world!</h1></body>

Remove elements

Sometimes you want to remove an element. Luckily you can use parent.removeChild(child):

document.body.removeChild(h1)
// <body></body>

Finding elements

You can use the following to find child elements:

Notice getElementsByTagName, getElementsByClassName and querySelectorAll doesn’t return array, but a NodeList, which you can‘t iterate with ES5 Array shortcuts. Here’s some workarounds.

Insert in-between elements

What if you want to add an element before another element? parent.insertBefore(child, before) to the rescue!

/*
* <body>
* <script src="main.js"></script>
* </body>
*/
document.body.insertBefore(h1, document.body.firstChild)
/*  <body>
* <h1>Hello world!</h1>
* <script src="main.js"></script>
* </body>
*/

Create list of elements

When you have a lot of data, it can be easier to create elements dynamically. Let’s see how:

Update list of elements

Many times you want to keep list of elements updated. Let’s add some more magic:

What is that sorcery?! Well, there’s two things happening here:

  1. There’s a hidden element._lookup = [] for finding child elements
    (which could be an object with id’s as well)
    Using the lookup we can reuse the elements already in the DOM and just update them.
  2. The setChildren(parent, children) function lets you provide a list of child elements. It cleverly compares them to the ones already attached to the parent and inserts/removes/reorders elements when needed.

You can also use setChildren to mount/unmount child elements:

setChildren(login, [
email,
!forgot && pass
])

RE:DOM

You might want to check out RE:DOM, my tiny view library. Some benefits:

  1. It’s much easier to create DOM elements with the el(query, …attributes/properties/text/children) helper function
  2. You can define views and how to update them. Since the browser knows which parts of the DOM to update, performance is increased.
  3. The list(parent, View, id, initData) helper makes it really easy to synchronize data with the DOM
import { el, text, mount } from 'redom'
class Hello {
constructor () {
this.el = el('h1.hello',
{ onclick: e => this.onclick(e) }
     'Hello ',
this.target = text('world'),
'!'
)
}
update (data) {
this.target.textContent = data
}
onclick (e) {
console.log('clicked ', this)
}
}
const hello = new Hello()
mount(document.body, hello)
hello.update('you')

Here’s an example with lists:

import { el, list, mount } from 'redom'
class Td {
constructor () {
this.el = el('td')
}
update (data) {
this.el.textContent = data
}
}

const Tr = list.extend('tr', Td)
const Table = list.extend('table', Tr)

const table = new Table

mount(document.body, table)

table.update([
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
])

I hope you learned new things about the DOM. You can follow me on Twitter and Github. Happy JavaScripting!