Introduction to Tree Diagram in D3.js

What is a Tree Diagram?

The ‘Tree layout’ is not only a distinct type of diagram. It also representates of D3’s family of hierarchical layouts. It’s designed to produce a ‘node-link’ diagram that lays out the connection between nodes in a method that displays the relationship of one node to another in a parent-child fashion.

The data required to produce this type of layout needs to describe the relationship. d3.layout.tree() is the starting point for tree layouts in D3. The call to this function returns an object that contains a bunch of methods to configure the layout and also provides methods to compute the layout.

Why do we need tree layout?

The tree layout produces tidy node-link diagrams of trees using the Reingold–Tilford “tidy” algorithm. With tree layout you can visualize your data in a node-relation like stucture. Which is tidy and easy to understand.

In this article we will just scratch the surface of tree layout in d3.js. Tree layout is one of the important feature of d3. Saying that you can assume that it does contain many elements to work with. We will go through them step by step.

basic_tree

Let us undestand what we have to do to get a basic tree structure like the above.

css file:

.node {
      cursor: pointer;
    }
    .node circle {
      fill: #00008b;
      stroke: red;
      stroke-width: 3px;
    }
    .node text {
      font: 12px sans-serif;
    }
    .link {
      fill: none;
      stroke: #ccc;
      stroke-width: 2px;
    }

Data:

var treeData = [{
      "name": "Parent Level",
      "parent": "null",
      "type": "black",
      "level": "red",
      "children": [{
        "name": "Level 2: A",
        "parent": "Top Level",
        "type": "steelblue",
      "level": "red",
        "children": [{
          "name": "Son of A",
          "parent": "Level 2: A"
        }, {
          "name": "Daughter of A",
          "parent": "Level 2: A"
        }]
      }, {
        "name": "Level 2: B",
        "parent": "Top Lvel",
        "type": "steelblue",
      "level": "red",
        "children": [{
          "name": "Son of B",
          "parent": "Level 2: B"
        }, {
          "name": "Son of B",
          "parent": "Level 2: B"
        }, {
          "name": "Son of B",
          "parent": "Level 2: B"
        }, ]
      }]
    }];

Js file:

 var margin = {
      top: 20,
      right: 120,
      bottom: 20,
      left: 120
    },
      width = 960 - margin.right - margin.left,
      height = 500 - margin.top - margin.bottom;

    var i = 0,
      duration = 750,
      root;

    var tree = d3.layout.tree()
      .size([height, width]);

    var diagonal = d3.svg.diagonal()
      .projection(function(d) {
        return [d.y, d.x];
      });

    var svg = d3.select("body").append("svg")
      .attr("width", width + margin.right + margin.left)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    root = treeData[0];
    root.x0 = height / 2;
    root.y0 = 0;

    update(root);

    function update(source) {

        var nodes = tree.nodes(root).reverse(),
        links = tree.links(nodes);

      nodes.forEach(function(d) {
        d.y = d.depth * 45;
      });

      var node = svg.selectAll("g.node")
        .data(nodes, function(d) {
          return d.id || (d.id = ++i);
        });

      var nodeEnter = node.enter().append("g")
        .attr("class", "node")
        .attr("transform", function(d) {
          return "translate(" + source.y0 + "," + source.x0 + ")";
        })
        .on("click", click);

      nodeEnter.append("circle")
.attr("r", function(d) { return d.value; })
.style("stroke", function(d) { return d.type; })
.style("fill", function(d) { return d.level; });

      nodeEnter.append("text")
        .attr("x", function(d) {
          return d.children || d._children ? -13 : 13;
        })
        .attr("dy", ".35em")
        .attr("text-anchor", function(d) {
          return d.children || d._children ? "end" : "start";
        })
        .text(function(d) {
          return d.name;
        })
        .style("fill-opacity", 1e-6);

      var nodeUpdate = node.transition()
        .duration(duration)
        .attr("transform", function(d) {
          return "translate(" + d.y + "," + d.x + ")";
        });

      nodeUpdate.select("circle")
        .attr("r", 5)
        .style("fill", function(d) {
          return d._children ? "red" : "#fff";
        });

      nodeUpdate.select("text")
        .style("fill-opacity", 5);

      var nodeExit = node.exit().transition()
        .duration(duration)
        .attr("transform", function(d) {
          return "translate(" + source.y + "," + source.x + ")";
        })
        .remove();

      nodeExit.select("circle")
        .attr("r", 1e-6);

      nodeExit.select("text")
        .style("fill-opacity", 1e-6);

      var link = svg.selectAll("path.link")
        .data(links, function(d) {
          return d.target.id;
        });

      link.enter().insert("path", "g")
        .attr("class", "link")
        .attr("d", function(d) {
          var o = {
            x: source.x0,
            y: source.y0
          };
          return diagonal({
            source: o,
            target: o
          });
        });

      link.transition()
        .duration(duration)
        .attr("d", diagonal);

      link.exit().transition()
        .duration(duration)
        .attr("d", function(d) {
          var o = {
            x: source.x,
            y: source.y
          };
          return diagonal({
            source: o,
            target: o
          });
        })
        .remove();
      nodes.forEach(function(d) {
        d.x0 = d.x;
        d.y0 = d.y;
      });
    }

    function click(d) {
      if (d.children) {
        d._children = d.children;
        d.children = null;
      } else {
        d.children = d._children;
        d._children = null;
      }
      update(d);
    } 

This is the basic code for the above example. We will try to look forward to other features based on this example in the upcoming articles.

If you find this article helpful, you can connect us in Google+ and Twitter.

Leave a Reply

Your email address will not be published. Required fields are marked *