How to display and hide links and nodes when clicking on a node in D3 Javascript
css, d3.js, force-layout, javascript
Solution
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:
- 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.
- 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.
- 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;
});
Problem
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.