I had a problem creating multiple force build graphs using d3 and reading data from a json file. I use a for loop to iterate over graphs, creating a separate div containing svg for each. The problem is that the force layout applies only to the last one created, so basically the rest just show a point in the upper left corner. I could solve this in part by putting a for loop at the end of each iteration, but I still lose the ability to interact with individual figures.
Find the code below, thanks in advance.
Cheers, Michael
var color = d3.scale.category20(); var force = new Array(); var div = new Array(); var svg = new Array(); var graph = new Array(); var link; var node; var width = 360; var height = 360; var brush = new Array(); var shiftKey; var count = 0; //loop through the different subsystems in the json-file for(name_subsystem in graphs) { //add a div for each subsystem div[count] = document.createElement("div"); div[count].style.width = "360px"; div[count].style.height = "360px"; div[count].style.cssFloat="left"; div[count].id = name_subsystem; document.body.appendChild(div[count]); //force is called. all attributes with default values are noted. see API reference on github. force[count] = d3.layout.force() .size([width, height]) .linkDistance(20) .linkStrength(1) .friction(0.9) .charge(-30) .theta(0.8) .gravity(0.1); div[count].appendChild(document.createTextNode(name_subsystem)); //create the svg rectangle in which other elements can be visualised svg[count] = d3.select("#"+name_subsystem) .on("keydown.brush", keydown) .on("keyup.brush", keyup) .append("svg") .attr("width", width) .attr("height", height) .attr("id",name_subsystem); brush[count] = svg[count].append("g") .datum(function() { return {selected: false, previouslySelected: false}; }) .attr("class", "brush"); //force is started force[count] .nodes(graphs[name_subsystem].nodes) .links(graphs[name_subsystem].links) .start(); //link elements are called, joined with the data, and links are created for each link object in links link = svg[count].selectAll(".link") .data(graphs[name_subsystem].links) .enter().append("line") .attr("class", "link") .style("stroke-width", function(d) { return Math.sqrt(d.thickness); }) .style("stroke", function(d){ if (d.linktype === 'reactant'){ return "black"; } else { return "red"; } }); //node elements are called, joined with the data, and circles are created for each node object in nodes node = svg[count].selectAll(".node") .data(graphs[name_subsystem].nodes) .enter().append("circle") .attr("class", "node") //radius .attr("r", 5) //fill .attr("fill", function(d) { if (d.type === 'metabolite') { return "blue"; } else { return "red"; } }) .on("mousedown", function(d) { if (!d.selected) { // Don't deselect on shift-drag. if (!shiftKey) node.classed("selected", function(p) { return p.selected = d === p; }); else d3.select(this).classed("selected", d.selected = true); } }) .on("mouseup", function(d) { if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false); }) .call(force[count].drag() .on("dragstart",function dragstart(d){ d.fixed=true; d3.select(this).classed("fixed",true); }) ); //gives titles to nodes. i do not know why this is separated from the first node calling. node.append("title") .text(function(d) { return d.name; }); //enable brushing of the network brush[count].call(d3.svg.brush() .x(d3.scale.identity().domain([0, width])) .y(d3.scale.identity().domain([0, height])) .on("brushstart", function(d) { node.each(function(d) { d.previouslySelected = shiftKey && d.selected; }); }) .on("brush", function() { var extent = d3.event.target.extent(); node.classed("selected", function(d) { return d.selected = d.previouslySelected ^ (extent[0][0] <= dx && dx < extent[1][0] && extent[0][1] <= dy && dy < extent[1][1]); }); }) .on("brushend", function() { d3.event.target.clear(); d3.select(this).call(d3.event.target); }) ); //applies force per step or 'tick'. force[count].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 dx; }) .attr("cy", function(d) { return dy; }); }); //with this it works partly //for (var i = 0; i < 5000; ++i)force[count].tick(); count++; }; function keydown() { if (!d3.event.metaKey) switch (d3.event.keyCode) { case 38: nudge( 0, -1); break; // UP case 40: nudge( 0, +1); break; // DOWN case 37: nudge(-1, 0); break; // LEFT case 39: nudge(+1, 0); break; // RIGHT } shiftKey = d3.event.shiftKey || d3.event.metaKey; } function keyup() { shiftKey = d3.event.shiftKey || d3.event.metaKey; }
edit: update the code after comments, still the same problem.