Sijia Xiao
Sijia Xiao

Reputation: 137

D3.js Tree:How to change the node color when clicking on it?

I want to change the color of nodes when clicking on it. It should be set in the "click" function.

However, in the click function, "d" is the node element, but to change node color I have to select the "circle" element, which is append to node using nodeEnter.append("circle"). How can I achieve it?

the html file:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node circle {
fill: #fff;
stroke: steelblue;
        stroke-width: 3px;
}

.node text {
font: 30px sans-serif;
}

.link {
fill: none;
stroke: #ccc;
        stroke-width: 7px;

}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>


var margin = {top: 20, right: 20, bottom: 20, left: 20},
    width = 1200 - margin.right - margin.left,
    height = 800 - margin.top - margin.bottom;

var i = 0,
    duration = 750,
    root;

var tree = d3.layout.tree();

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

    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 + ")");


    d3.json("parse_tree.json", function(error, parse_tree) {
            if (error) throw error;

            root = parse_tree;
            root.x0 = height / 2;
            root.y0 = 0;

            tree.size([width, height]);

            update(root);
            });

function update(source1) {
    var originalConsole = console;
    var nodes = tree.nodes(root);
    var links = tree.links(nodes);

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

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



    // Enter any new nodes at the parent's previous position.
    var nodeEnter = node.enter().append("g")
        .attr("class", "node")
        .attr("transform", function(d) { return "translate(" + source1.y0 + "," + source1.x0 + ")"; })
        .on("click", click);

    nodeEnter.append("circle")
        .attr("r", 1e-6)
        .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

    nodeEnter.append("text")
        .attr("x", function(d) { return d.children || d._children ? -20 : 20; })
        .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);


    // Transition nodes to their new position.
    var nodeUpdate = node.transition()
        .duration(duration)
        .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

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

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

    // Transition exiting nodes to the parent's new position.
    var nodeExit = node.exit().transition()
        .duration(duration)
        .attr("transform", function(d) { return "translate(" + source1.y + "," + source1.x + ")"; })
        .remove();

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

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

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

             );

    // Enter any new links at the parent's previous position.
    link.enter().insert("path", "g")
        .attr("class", "link")
        .attr("d", function(d) {
                var o = {x: source1.x0, y: source1.y0};
                return diagonal({source: o, target: o});
                })
    .style("stroke-width", "3px")
        .style("stroke", "green");
    // Transition links to their new position.
    link.transition()
        .duration(duration)
        .attr("d", diagonal);

    // Transition exiting nodes to the parent's new position.
    link.exit().transition()
        .duration(duration)
        .attr("d", function(d) {
                var o = {x: source1.x, y: source1.y};
                return diagonal({source: o, target: o});
                })
    .remove();

    // Stash the old positions for transition.
    nodes.forEach(function(d) {
            d.x0 = d.x;
            d.y0 = d.y;

            });
    for(var k = 0; k < 1000; k++)
        flag = 0;
}

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


</script>

the json file:

         {"name":"VP",
          "size":"89",
         "children": [
         {"name":"VBP",
          "size":"15",
         "children":[{"name":"are", "size":"38"}]
         },
         {"name":"NP",
          "size":"83",
         "children": [
         {"name":"DT",
          "size":"29",
         "children":[{"name":"a", "size":"53"}]
         },
         {"name":"NN",
          "size":"50",
         "children":[{"name":"boy", "size":"99"}]
         }
         ]
         }
         ]
         }

Upvotes: 1

Views: 6048

Answers (1)

mgraham
mgraham

Reputation: 6207

d is the data, not the node

this in the click function is the dom node

d3.select(this) is the selection that picks just that node as a d3 selection

so to get a child circle element in a selection, you would do d3.select(this).select("circle")

Now you'd think you'd just add a style("fill", "red") or whatever to this and job done, but as this is the oft-forked example from Mike Bostock that style would get overridden again in the transition update. So what you need to do is set a property on the data and then get that picked up by the drawing in the enter/update/exit sequence.

In the end all you need in the click function is:

d.clicked = true;

and in the nodeUpdate section do this -->

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

Note this doesn't do anything about un-highlighting a node. (You'd have to toggle the previous d.clicked boolean value)

Upvotes: 4

Related Questions