Skip to content

Imitating the DOM tree #8

Open
Open
@mateogianolio

Description

@mateogianolio

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>

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions