You need to append a group to your SVG using .append('svg:g'); after declaring your SVG. Unfortunately you cannot apply transforms to the svg element directly when it has child elements (as they need to scale also).
You can see it in action here - http://jsfiddle.net/s7aLv276/ <- New Version
//Append a SVG to the body of the html page. Assign this SVG as an object to svg
var svg = d3.select('body').append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom)
.append('svg:g'); // <- Add your group so you can transform all elements together
The entire code:
//Constants for the SVG
var width = 500,
height = 500;
//Set up the colour scale
var color = d3.scale.category20();
//Set up the force layout
var force = d3.layout.force()
.charge(-120)
.linkDistance(80)
.size([width, height]);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
//Append a SVG to the body of the html page. Assign this SVG as an object to svg
var svg = d3.select('body').append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom)
.append('svg:g');
//Read the data from the mis element
var mis = document.getElementById('mis').innerHTML;
graph = JSON.parse(mis);
force.drag().on("dragstart", function() { d3.event.sourceEvent.stopPropagation(); });
//Creates the graph data structure out of the json data
force.nodes(graph.nodes)
.links(graph.links)
.start();
//Create all the line svgs but without locations yet
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("marker-end", "url(#suit)") //Added
;
//Do the same with the circles for the nodes - no
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 8)
.style("fill", function(d) {
return color(d.group);
})
.call(force.drag);
function zoomed() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
force.start();
}
function dragged(d) {
d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this).classed("dragging", false);
}
//Now we are giving the SVGs co-ordinates - the force layout is generating the co-ordinates which this code is using to update the attributes of the SVG elements
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;
});
});
//---Insert-------
svg.append("defs").selectAll("marker")
.data(["suit", "licensing", "resolved"])
.enter().append("marker")
.attr("id", function(d) {
return d;
})
.attr("viewBox", "0 -5 10 10")
.attr("refX", 25)
.attr("refY", 0)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5 L10,0 L0, -5")
.style("stroke", "#4679BD")
.style("opacity", "0.6");
//---End Insert---
* Updated to add pan/zoom on nodes *
It looks like we weren't attaching a drag handler to the Force object. I've added the following line:
force.drag().on("dragstart", function() { d3.event.sourceEvent.stopPropagation(); });
The graph should allow you to now drag individual nodes, as well as pan and zoom the entire graph.
See it in action - http://jsfiddle.net/s7aLv276/