I've been working on modified force directed graph and having some problems with adding text/label onto links where the links are not properly aligned to nodes. How to fix it?
And how I can add an event listener to an SVG text element? Adding .on("dblclick",function(d) {....}
just doesn't work.
Here's the code snippet:
<style type="text/css"> .link { stroke: #ccc; } .routertext { pointer-events: none; font: 10px sans-serif; fill: #000000; } .routertext2 { pointer-events: none; font: 9px sans-serif; fill: #000000; } .linktext { pointer-events: none; font: 9px sans-serif; fill: #000000; } </style> <div id="canvas"> </div> <script type="text/javascript" src="d3/d3.js"></script> <script type="text/javascript" src="d3/d3.layout.js"></script> <script type="text/javascript" src="d3/d3.geo"></script> <script type="text/javascript" src="d3/d3.geom.js"></script> <script type="text/javascript"> var w = 960, h = 600, size = [w, h]; // width height var vis = d3.select("#canvas").append("svg:svg") .attr("width", w) .attr("height", h) .attr("transform", "translate(0,0) scale(1)") .call(d3.behavior.zoom().on("zoom", redraw)) .attr("idx", -1) .attr("idsel", -1) ; var routers = { nodes: [ {id:0, name:"ROUTER-1", group:1, ip: "123.123.123.111", x:394.027, y:450.978,outif:"ge-0/1/0.0",inif:""}, {id:1, name:"ROUTER-2", group:1, ip: "123.123.123.222", x:385.584, y:351.513,outif:"xe-4/2/0.0",inif:"ge-5/0/3.0"}, {id:2, name:"ROUTER-3", group:1, ip: "123.123.123.333", x:473.457, y:252.27,outif:"ae1.0",inif:"xe-1/0/1.0"}, {id:3, name:"ROUTER-4", group:2, ip: "123.123.123.444", x:723.106, y:266.569,outif:"as0.0",inif:"ae1.0"}, {id:4, name:"ROUTER-5", group:3, ip: "123.123.123.555", x:728.14, y:125.287,outif:"so-4/0/2.0",inif:"as1.0"}, {id:5, name:"ROUTER-6", group:3, ip: "123.123.123.666", x:738.975, y:-151.772,outif:"",inif:"PO0/2/2/1" } ], links: [ {source:0, target:1, value:3, name:'link-1',speed:"1000mbps", outif:"ge-0/1/0.0",nextif:"ge-5/0/3.0"}, {source:1, target:2, value:3, name:'link-2',speed:"10Gbps", outif:"xe-4/2/0.0",nextif:"xe-1/0/1.0"}, {source:2, target:3, value:3, name:'link-3',speed:"20Gbps", outif:"ae1.0",nextif:"xe-1/2/1.0"}, {source:3, target:4, value:3, name:'link-4',speed:"1Gbps", outif:"as0.0",nextif:"as1.0"}, {source:4, target:5, value:3, name:'link-5',speed:"OC3", outif:"so-4/0/2.0",nextif:"PO0/2/2/1"} ] }; var force = d3.layout.force() .nodes(routers.nodes) .links(routers.links) .gravity(0) .distance(100) .charge(0) .size([w, h]) .start(); var link = vis.selectAll("g.link") .data(routers.links) .enter().append("svg:g"); link.append("svg:line") .attr("class", "link") .attr("title", function(d) { return "From: "+d.outif+", To: "+d.nextif }) .attr("style", "stroke:#00d1d6;stroke-width:4px") .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; }); link.append("svg:text") .attr("class", "linktext") .attr("dx", function(d) { return d.source.x; }) .attr("dy", function(d) { return d.source.y; }) .text("some text to add..."); var node = vis.selectAll("g.node") .data(routers.nodes) .enter() .append("svg:g") .attr("id", function(d) { return d.id;}) .attr("title", function(d) {return d.ip}) .attr("class", "node") .attr("x", function(d) { return d.x; }) .attr("y", function(d) { return d.y; }) .on("dblclick",function(d) { alert('router double-clicked'); d3.event.stopPropagation(); }) .on("mousedown", function(d) { if (d3.event.which==3) { d3.event.stopPropagation(); alert('Router right-clicked'); } }) .call(force.drag); node.append("svg:image") .attr("class", "node") .attr("xlink:href", "router.png") .attr("x", -24) .attr("y", -18) .attr("width", 48) .attr("height", 36); node.append("svg:text") .attr("class", "routertext") .attr("dx", -30) .attr("dy", 20) .text(function(d) { return d.name }); node.append("svg:text") .attr("class", "routertext2") .attr("dx", 0) .attr("dy", -20) .attr("title", "some title to show....") .text(function(d) { return d.outif }) .on("click", function(d,i) {alert("outif text clicked");}) .call(force.drag); node.append("svg:text") .attr("class", "routertext2") .attr("dx", -40) .attr("dy", 30) .text(function(d) { return d.inif }); 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("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); }); function redraw() { vis.attr("transform", "translate(" + d3.event.translate + ")" + "scale(" + d3.event.scale + ")"); }; </script>
Use a smaller example outside of D3 to see how the SVG stuff works. Then just rebuild this structure using D3 and your custom data.
<html> <body> <svg width="600px" height="400px"> <defs> <!-- DEFINE AN ARROW THAT WE CAN PLACE AT THE END OF EDGES. --> <!-- USE REFX TO MOVE THE ARROW'S TIP TO THE END OF THE PATH. --> <marker orient="auto" markerHeight="12" markerWidth="12" refY="0" refX="9" viewBox="0 -5 10 10" id="ARROW_ID" style="fill: red; fill-opacity: 0.5;"> <path d="M0, -5L10, 0L0, 5"></path> </marker> </defs> <!-- DEFINE A PATH. SET ITS END MARKER TO THE ARROW'S ID. --> <!-- SET FILL NONE TO DRAW A LINE INSTEAD OF A SHAPE. --> <path d="M100,100 A300,250 0 0,1 500,300" style="fill:none; stroke:grey; stroke-width:2px;" id="PATH_ID" marker-end="url(#ARROW_ID)" /> <!-- DEFINE A TEXT ELEMENT AND SET FONT PROPERTIES. --> <!-- USE DY TO MOVE TEXT ABOVE THE PATH. --> <text style="text-anchor:middle; font: 16px sans-serif;" dy="-12"> <!-- DEFINE A TEXT PATH FOLLOWING THE PATH DEFINED ABOVE. --> <!-- USE STARTOFFSET TO CENTER TEXT. --> <textPath xlink:href="#PATH_ID" startOffset="50%">Centered edge label</textPath> </text> </svg> </body> </html>
Have you experimented with creating text elements separately in a standalone (simpler) example? It should give you a better feeling for how the different attributes control positioning.
For vertical alignment, use the "dy" attribute:
Using em units is nice because it will scale automatically based on the font size. If you don't specify units (such as -20 in your code), it defaults to pixels.
For horizontal alignment, use the "text-anchor" attribute:
There's also the "dx" attribute, which is tempting to use for padding. However, I wouldn't recommend it because there is a bug in Firefox and Opera that cause it to not work as expected in conjunction with text-anchor middle or end.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With