Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Imitating the DOM tree #8

Open
mateogianolio opened this issue Feb 22, 2016 · 2 comments
Open

Imitating the DOM tree #8

mateogianolio opened this issue Feb 22, 2016 · 2 comments

Comments

@mateogianolio
Copy link
Member

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>
@SkyHacks
Copy link

Great stuff Mateo! I'm loving these articles!!

@mateogianolio
Copy link
Member Author

Thanks dude!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants