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.

Original source