Description
The document object model (DOM) is essentially a tree (data structure), containing nodes with a number of optional attributes such as id
or class
.
Each node in the tree can have one parent and several children and these properties connect the nodes together to form a tree.
Consider this boilerplate HTML (left) and its corresponding tree (right):
<html> <head> <title></title> <script><script> </head> <body> </body> </html> |
html / \ head body / \ title script |
Note that the entire tree structure can be derived from any node in the tree by visiting all of its parents and children. This means that one single node will suffice to describe the entire tree.
So let's make a Node
class:
class Node {
constructor(name, attribs) {
this.name = name;
this.children = [];
this.attribs = attribs;
}
}
var html = new Node('html');
console.log(html);
// Node { name: 'html', children: [] }
For inheritance to work we need to extend Node
with a method that allows us to append child nodes:
append(node) {
node.parent = this;
this.children.push(node);
}
var html = new Node('html');
html.append(new Node('head'));
console.log(html);
/* Node {
name: 'html',
children: [ Node { name: 'head', children: [], parent: [Circular] } ] } */
Neat! We just learned how to make a circular object. How do we traverse it? With generators, we can do it recursively in a few lines of code:
*traverse(node) {
if (node === undefined)
node = this;
yield node;
for (var child of node.children)
yield *this.traverse(child);
}
var html = new Node('html');
var head = new Node('head');
head.append(new Node('title'));
html.append(head);
html.append(new Node('body'));
for (var node of html.traverse())
console.log(node.name);
// html
// head
// title
// body
Finally, we can add a simple rendering method:
toString(node) {
if (node === undefined)
node = this;
var out = '<' + node.name,
attributes = [];
for (var key in node.attribs)
if (node.attribs.hasOwnProperty(key))
attributes.push(' ' + key + '="' + node.attribs[key] + '"');
out += attributes.join('') + '>';
for (var child of node.children)
out += this.toString(child);
out += '</' + node.name + '>';
return out;
}
var html = new Node('html', { class: 'cool' });
var head = new Node('head');
head.append(new Node('title'));
html.append(head);
html.append(new Node('body'));
console.log(html.toString());
// <html class="cool"><head><title></title></head><body></body></html>