Custom body CSS with d3-mitch-tree

Welcome to part 2/10 (yes, eight more tutorials to come!) on d3-mitch-tree. Everything is available on my Github repo as usual.

 

  1. d3-mitch-tree 101: Create a Basic Tree
  2. d3-mitch-tree 101: Add custom body CSS (This tutorial)

 

1 – Setup

In your working directory, create the following file structure:

.
├── css
│   ├── d3-mitch-tree.css
│   └── d3-mitch-tree-theme-default.css
│   └── custom_tree.css
├── js
│   ├── d3-mitch-tree.js
│   └── simple_tree.js
|   └── load_tree.js
└── simple_tree.html

 

You can download the css files d3-mitch-tree.css and d3-mitch-tree-theme-default.css directly from my Github repo here: for some reason the ones in the original documentation did not work very well for me. Similarly, you can download d3-mitch-tree.js from my repo.

 

2 – Simple_tree.js

Now this file will contain your tree in JSON format. For the sake of formatting, let’s say the tree contains grades from two assignments, and we want to color them depending on whether the grade was good (< 65%) or bad.

var data = {
    "id"    : 1,
    "title" : "grades",
    "children": [
        {
            "id"    : 2,
            "title" : "assignment1",
            "grade" : 63,
            "max"   : 100,
        },
        {
            "id"    : 3,
            "title" : "assignment2",
            "grade" : 95,
            "max"   : 100,
        }
    ]
};

 

3- Simple_tree.html

We are building up on knowledge from part one so I will not get into most of what’s inside d3.mitchTree.boxedTree(), but notice the condition inside setBodyDisplayTextAccessor(): return the grade if the property exists, otherwise, return the string “Student Grades”.

<html>
    <head>
        <title>Simple JavaScript Tree</title>
        <meta charset="utf-8">

        <link rel="stylesheet" href="css/d3-mitch-tree.css">
        <link rel="stylesheet" href="css/d3-mitch-tree-theme-default.css">
        <link rel="stylesheet" href="css/custom_tree.css"> <!-- my custom css -->

        <script src="js/d3-mitch-tree.js"></script>
        <script src="js/simple_tree.js"></script>
        <script src="js/load_tree.js"></script>
    </head>

    <body>
        <!-- div that will contain the tree -->
        <div id="my_tree" style="width:800px;height:500px;border:1px solid grey;"></div>

        <script>
            // Create a new d3.mitchTree object and set the data source
            var treePlugin = new d3.mitchTree.boxedTree()
                .setData(data) // the data variable comes from simple_tree.js
                .setElement(document.getElementById("my_tree"))
                .setIdAccessor(function(data) {
                    return data.id;
                })
                .setChildrenAccessor(function(data) {
                    return data.children;
                })
                .setBodyDisplayTextAccessor(function(data) {
                    // Return the grade property only if it is defined
                    if (data.grade != null) {
                        return data.grade;
                    }
                    return "Student Grades";
                })
                .setTitleDisplayTextAccessor(function(data) {
                    return data.title;
                })
                .initialize();

                // Override the core update method, to call our custom update method
                treePlugin.update = function(nodeDataItem){
                    this.__proto__.update.call(this, nodeDataItem);
                    updateTreeClasses(this);
                }
                updateTreeClasses(treePlugin);
        </script>
    </body>
</html>

 

Another addition is the override method updateTreeClasses() that is contained inside the file js/load_tree.js (the 3rd script to be loaded inside <head>…</head>). The function basically says: see if the tree node has a grade property. If it doesn’t add the extra CSS class “parent” to it. Otherwise, depending on the value of the grade property, add the extra CSS class “good_grade” or “bad_grade”.

// Let's override the colors depending on the grade
function updateTreeClasses(treePlugin) {
    treePlugin.getPanningContainer().selectAll("g.node")
        .attr("class", function(data, index, arr) {

            var grade = data.data.grade;

            if (grade == null) {
                var grade_class = "parent";
            } else if (grade > 65) {
                var grade_class = "good_grade";
            } else {
                var grade_class = "bad_grade";
            }

            // Add the grade CSS class on top of the other tree node classes
            // if required
            var existingClasses = this.getAttribute("class");
            if (!existingClasses)
                return grade_class;
            var hasClassAlready = (" " + existingClasses + " ").indexOf(" " + grade_class + " ") > -1;
            if (hasClassAlready)
                return existingClasses;
            return existingClasses + " " + grade_class;
        });
}

 

One last piece is missing in this puzzle: what do the extra css classes do? They are inside the new css file css/custom_tree.css, which changes the colors of the stroke and fill dependently.

.mitch-d3-tree.boxed-tree.default .good_grade .body-group .body-box {
    stroke  : #0000b3;
    fill    : #ffffff;
    stroke-width: 3;
}
.mitch-d3-tree.boxed-tree.default .bad_grade .body-group .body-box {
    stroke  : #df1019;
    fill    : #ffffff;
    stroke-width: 3;
}
.mitch-d3-tree.boxed-tree.default .parent .body-group .body-box {
    stroke  : #000000;
    fill    : #ffffff;
    stroke-width: 3;
}

 

Testing

Now, in your browser, open the file simple_tree.html. This is how it should look like

Et voilà! It does seem like a lot of code to change leaf colors, but this library is pretty flexible. Once you get the hang of it you can do a lot of cool stuff.