Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to display and hide links and nodes when clicking on a node in D3 Javascript

I am trying to follow this D3 Javascript link: > http://bl.ocks.org/mbostock/1093130 to understand how the click event works. What I am trying to do is when clicking on the blue node, the other two nodes and their links should display. When I click on the same node again, the two nodes and their links must hide. If I click on one of the other two nodes, nothing should happen.

Here is the JSON file:

{
"nodes": [
    {
        "name": "Node1",
        "group": 2
    },
    {
        "name": "Node2",
        "group": 1
    },
    {
        "name": "Node3",
        "group": 1
    }
],
"links": [
    {
        "source": 0,
        "target": 1,
        "value": 2
    },
    {
        "source": 0,
        "target": 2,
        "value": 2
    }
]
}

Here is my code:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<style>

.node {
stroke: #fff;
stroke-width: 1.5px;
}

.link {
stroke: #999;
stroke-opacity: .6;
}

</style>
<body>
<p>Are you there!!!</p>
<script type="text/javascript" src="d3/d3.v3.js"></script>
<script>

var width = 960,
height = 500;

d3.json("sample.json", function(error, graph) {

var color = d3.scale.category20();

var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .size([width, height]);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
force
.nodes(graph.nodes)
.links(graph.links)
.start();

var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });

var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function(d) { return color(d.group); })
.call(force.drag);

node.append("title")
.text(function(d) { return d.name; });

force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });

node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
</script>
</body>
</html>

Could anyone help me solve this issue please. Thank you.

like image 811
user2864315 Avatar asked Nov 27 '13 17:11

user2864315


1 Answers

You can break down the steps required to toggle on/off the visibility of a node's neighbors/edges by clicking that node as follows:

  1. You need to class links based on the names of the sources/targets, and give nodes ids based on their names. This makes determining which nodes/links to toggle trivial.
  2. You need to store whether or not each node is currently "active," which you can use to determine whether or not you should hide/show the node's neighbors.
  3. You need to add an on("click", function(){ ... }) to each node.

I created a JSFiddle that demonstrates this approach. The main points to highlight are that, first, I create a dictionary to map node names to the names of their neighbors, which makes determining which links to show/hide easy:

// Set up dictionary of neighbors
var node2neighbors = {};
for (var i =0; i < json.nodes.length; i++){
    var name = json.nodes[i].name;
    node2neighbors[name] = json.links.filter(function(d){
            return d.source.name == name || d.target.name == name;
        }).map(function(d){
            return d.source.name == name ? d.target.name : d.source.name;
        });
}

To get the names of the neighbors of a node n, I am first filtering all the links that include n, and then extracting the name of the node that is not n in each of those links.

Next, here's the logic performed when you click on a node. I'm using an array of nodes to keep track of the nodes we want to toggle on/off (the OP said this behavior should only apply for the blue node "Node1"):

var clickableNodes = ["Node1"];
nodes.filter(function(n){ return clickableNodes.indexOf(n.name) != -1; })
        // Determine if current node's neighbors and their links are visible
        var active   = n.active ? false : true // toggle whether node is active
        , newOpacity = active ? 0 : 1;

        // Extract node's name and the names of its neighbors
        var name     = n.name
        , neighbors  = node2neighbors[name];

        // Hide the neighbors and their links
        for (var i = 0; i < neighbors.length; i++){
            d3.select("circle#" + neighbors[i]).style("opacity", newOpacity);
            d3.selectAll("line." + neighbors[i]).style("opacity", newOpacity);
        }
        // Update whether or not the node is active
        n.active = active;
    });
like image 116
mdml Avatar answered Oct 12 '22 17:10

mdml