简体   繁体   中英

d3js search nodes and its child

My project is to do a force-directed graph with a textbox to search for any node. When users search for something, I want to highlight that node and its neighbours, like they do here when you hover on a node.

My source code :

 var width = 900, height = 590; // var zoom = d3.behavior.zoom().scaleExtent([0.1,5]).on("zoom"); var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height) .append('g'); d3.json("https://raw.githubusercontent.com/khalidal-walid/fyp/master/countries.json", function(data) { // Extract the nodes and links from the data. var nodes = data["nodes"]; var links = data["links"]; //CONNECTIONS var hash_lookup = []; nodes.forEach(function(d, i) { hash_lookup[d.country] = d; }); links.forEach(function(d, i) { d.source = hash_lookup[d.source]; d.target = hash_lookup[d.target]; }); // Now we create a force layout object and define its properties. // Those include the dimensions of the visualization and the arrays of nodes and links. var force = d3.layout.force() .size([width, height]) .nodes(d3.values(nodes)) .links(links) .on('tick', tick) .linkDistance(100) .gravity(.15) .friction(.8) .linkStrength(1) .charge(-425) .chargeDistance(600) .start(); //LINKS var link = svg.selectAll('.link') .data(links) .enter().append('line') .attr('class', 'link'); //NODES var node = svg.selectAll('.node') .data(force.nodes()) .enter().append('circle') .attr('class', 'node') .attr('r', width * 0.01) //LABELS var text_center = false; var nominal_text_size = 12; var max_text_size = 22; var nominal_base_node_size = 8; var max_base_node_size = 36; var size = d3.scale.pow().exponent(1) .domain([1, 100]) .range([8, 24]); var text = svg.selectAll(".text") .data(nodes) .enter().append("text") .attr("dy", ".35em") .style("font-size", nominal_text_size + "px") if (text_center) text.text(function(d) { return d.code; }) .style("text-anchor", "middle"); else text.attr("dx", function(d) { return (size(d.size) || nominal_base_node_size); }) .text(function(d) { return '\ ' + d.code; }); //ZOOM AND PAN // function redraw() { // svg.attr("transform", // "translate(" + d3.event.translate + ")" // + " scale(" + d3.event.scale + ")"); // } // var drag = force.drag() // .on("dragstart", function(d) { // d3.event.sourceEvent.stopPropagation(); // }); //NODES IN SPACE function tick(e) { text.attr("transform", function(d) { return "translate(" + dx + "," + dy + ")"; }); node.attr('cx', function(d) { return dx; }) .attr('cy', function(d) { return dy; }) .call(force.drag); 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; }); }; //AUTOCOMPLETE SEARCH var optArray = []; for (var i = 0; i < nodes.length - 1; i++) { optArray.push(nodes[i].code); } optArray = optArray.sort(); window.searchNode = searchNode; function searchNode() { var selectedVal = document.getElementById('search').value; if (selectedVal == 'none') {} else { var selected = node.filter(function(d, i) { return d.code != selectedVal; }); var selectedText = text.filter(function(d, i) { return d.code != selectedVal; }); selected.style("opacity", "0"); selectedText.style("opacity", "0"); var link = svg.selectAll(".link") link.style("opacity", "0"); d3.selectAll(".node, .link, .text").transition() .duration(3000) .style("opacity", '1'); var selectedNode = node .filter(function(d, i) { return d.code == selectedVal; }) .datum(); var scale = zoom.scale(); var desiredPosition = { x: 200, y: 200 }; // constants, set to svg center point zoom.translate([desiredPosition.x - selectedNode.x * scale, desiredPosition.y - selectedNode.y * scale]); zoom.event(svg); } } })
 .node { stroke: #aaa; stroke-width: 2px; } .link { stroke: #aaa; stroke-width: 2px; }
 <script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'></script> <div class="ui-widget"> <input id="search" , size='54' , autocomplete="off"> <button type="button" onclick="searchNode()">Search</button> </div>

I think the following is what you want. I check first whether any node matches the search term, then if that's true, I find all links that do not involve the matching node, and hide them. At the same time, I populate highlightCodes , an array that contains the matching code and those of all direct neighbours. Then I hide all nodes that do not match the highlightCodes array.

 var width = 900, height = 590; // var zoom = d3.behavior.zoom().scaleExtent([0.1,5]).on("zoom"); var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height) .append('g'); d3.json("https://raw.githubusercontent.com/khalidal-walid/fyp/master/countries.json", function(data) { // Extract the nodes and links from the data. var nodes = data["nodes"]; var links = data["links"]; //CONNECTIONS var hash_lookup = []; nodes.forEach(function(d, i) { hash_lookup[d.country] = d; }); links.forEach(function(d, i) { d.source = hash_lookup[d.source]; d.target = hash_lookup[d.target]; }); // Now we create a force layout object and define its properties. // Those include the dimensions of the visualization and the arrays of nodes and links. var force = d3.layout.force() .size([width, height]) .nodes(d3.values(nodes)) .links(links) .on('tick', tick) .linkDistance(100) .gravity(.15) .friction(.8) .linkStrength(1) .charge(-425) .chargeDistance(600) .start(); //LINKS var link = svg.selectAll('.link') .data(links) .enter() .append('line') .attr('class', 'link'); //NODES var node = svg.selectAll('.node') .data(force.nodes()) .enter() .append('circle') .attr('class', 'node') .attr('r', width * 0.01); //LABELS var text_center = false; var nominal_text_size = 12; var max_text_size = 22; var nominal_base_node_size = 8; var max_base_node_size = 36; var size = d3.scale.pow() .exponent(1) .domain([1, 100]) .range([8, 24]); var text = svg.selectAll(".text") .data(nodes) .enter() .append("text") .attr("dy", ".35em") .style("font-size", nominal_text_size + "px"); if(text_center) { text.text(function(d) { return d.code; }) .style("text-anchor", "middle"); } else { text.attr("dx", function(d) { return (size(d.size) || nominal_base_node_size); }) .text(function(d) { return '\ ' + d.code; }); } //ZOOM AND PAN // function redraw() { // svg.attr("transform", // "translate(" + d3.event.translate + ")" // + " scale(" + d3.event.scale + ")"); // } // var drag = force.drag() // .on("dragstart", function(d) { // d3.event.sourceEvent.stopPropagation(); // }); //NODES IN SPACE function tick(e) { text.attr("transform", function(d) { return "translate(" + dx + "," + dy + ")"; }); node.attr('cx', function(d) { return dx; }) .attr('cy', function(d) { return dy; }) .call(force.drag); 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; }); }; //AUTOCOMPLETE SEARCH window.searchNode = searchNode; function resetSelection() { node.style('opacity', null); link.style('opacity', null); text.style('opacity', null); } function searchNode() { var selectedVal = document.getElementById('search').value; if(selectedVal == 'none') { return resetSelection(); } var matchingNode = nodes.find(function(d) { return d.code === selectedVal; }); if(matchingNode === undefined) { return resetSelection(); } var matchingCode = matchingNode.code; var highlightCodes = []; link .filter(function(d) { if(d.source.code === matchingCode || d.target.code === matchingCode) { highlightCodes.push(d.source.code, d.target.code); return false; } return true; }) .transition() .duration(500) .style("opacity", 0.3); d3.selectAll(".node, .text") .filter(function(d) { return highlightCodes.indexOf(d.code) === -1; }) .transition() .duration(500) .style("opacity", 0.3); } });
 .node { stroke: #aaa; stroke-width: 2px; } .link { stroke: #aaa; stroke-width: 2px; }
 <script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'></script> <div class="ui-widget"> <input id="search" size='54' autocomplete="off"> <button type="button" onclick="searchNode()">Search</button> </div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM