Using 'textPath' to create line labels in force-directed graph -- problem in Safari

3,918 views
Skip to first unread message

Neil Benn

unread,
Jul 26, 2011, 12:53:53 PM7/26/11
to d3-js
Hi all,

I'm new to D3 and already enjoying experimenting with it and getting
more familiar with it.

I have been using the 'force.js' example distributed with the code as
a starting point and just adding little modifications here and there.

One modification I am trying is placing text labels on the lines in
the graph. I'm also new to SVG but I noticed I can use 'textPath' to
put text on lines. So here are snippets of the code that I modified
from the original 'force.js' example:

First, the original:
------

var link = vis.selectAll("line.link")
.data(json.links)
.enter().append("svg:line")
.attr("class", "link")
.style("stroke-width", function(d) { return
Math.sqrt(d.value); })
.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; });

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; });
});

------

Now the modified version (in the JSON data I added a "label"
attribute):
------

var link = vis.selectAll("path.link")
.data(json.links)
.enter().append("svg:path")
.attr("class", "link")
.attr("id",
function(d) {
return "path"+d.source.index+"_"+d.target.index;
})
.attr("d",
function(d) {
return moveto(d) + lineto(d);
});

function moveto (d) {
return "M"+d.source.x+","+d.source.y;
};

function lineto (d) {
return " L"+d.target.x+","+d.target.y;
};

var label = vis.selectAll("text")
.data(json.links)
.enter().append("svg:text")
.attr("font-size", 10)
.append("svg:textPath")
.attr("xlink:href",
function(d) {
return "#path"+d.source.index+"_"+d.target.index;
})
.text(function(d){ return d.label; });

force.on("tick", function() {
link.attr("d",
function(d) {
return moveto(d) + lineto(d);
});

node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});

------

This works fine in Firefox -- the label is displayed on the line and
even as the nodes move around during the "ticking" of the graph, the
labels remain firmly placed on the line. Not so in Safari.

For some reason in Safari, when the graph loads, the labels appear but
then as the nodes move around, the labels are left behind and the
lines appear without any labels.

Does anyone have any idea about why the behaviour is different in
Safari? And any ideas about a possible fix?

Many thanks for any help or suggestions,
Neil

Jason Davies

unread,
Jul 27, 2011, 1:40:17 PM7/27/11
to d3...@googlegroups.com
On Tue, Jul 26, 2011 at 09:53:53AM -0700, Neil Benn wrote:
> I have been using the 'force.js' example distributed with the code as
> a starting point and just adding little modifications here and there.
>
> One modification I am trying is placing text labels on the lines in
> the graph. I'm also new to SVG but I noticed I can use 'textPath' to
> put text on lines. So here are snippets of the code that I modified
> from the original 'force.js' example:
[snip]

> This works fine in Firefox -- the label is displayed on the line and
> even as the nodes move around during the "ticking" of the graph, the
> labels remain firmly placed on the line. Not so in Safari.
>
> For some reason in Safari, when the graph loads, the labels appear but
> then as the nodes move around, the labels are left behind and the
> lines appear without any labels.
>
> Does anyone have any idea about why the behaviour is different in
> Safari? And any ideas about a possible fix?

I'm not sure why it doesn't work as-is in WebKit, but I tried this fix and it
worked. Add the following to your "tick" handler:

label


.attr("xlink:href",
function(d) {
return "#path"+d.source.index+"_"+d.target.index;
})

It seems like setting the href attribute (even though it doesn't change) forces
the text to move.

--
Jason Davies, http://www.jasondavies.com/

Neil Benn

unread,
Jul 29, 2011, 5:05:23 AM7/29/11
to d3-js
Many thanks Jason, that works! And I guess it makes sense that I
should have to include this in the "tick" handler (although, as you,
the href attribute isn't actually changing)

Regards,
Neil

TomS72

unread,
Dec 15, 2012, 5:39:48 AM12/15/12
to d3...@googlegroups.com
Hi all,

i have also an force-drected graph.
I want to add Lables to the linklines, but i have no idea after many, many tries.

Here is the JavaScript:
function initGraph(graphJSON, width, height) {

   var svg = d3.select("body").append("svg")
   .attr("width", width)
   .attr("height", height)
   ;

   var force = d3.layout.force()
   .distance(height/10)
   .charge(-height)
   .theta(0.1)
   .gravity(0.18)
   .linkStrength(5)
   .size([width, height]);

   d3.json(graphJSON, function(json) {
      force
      .nodes(json.nodes)
      .links(json.links)
      .start();

      // Nodes
      var node = svg.selectAll(".node")
      .data(json.nodes)
      .enter().append("g")
      .attr("class", "node")
      .call(force.drag);

      node.append("image")
      .attr("xlink:href", "classIcon.ico")
      .attr("x", -28)
      .attr("y", -16)
      .attr("width", 32)
      .attr("height", 63)
      .on("mouseout", function(d, i) {
         d3.select(this).transition().attr("width", 32);
      })
      .on("mouseover", function(d, i) {
         d3.select(this).transition().attr("width", 48);
      })
      ;

      node.append("svg:text")
      .attr("x", 10)
      .attr("y", 25)
      .style("font-size", "90%")
      .attr("text-anchor", "start")   //"end", "start", "middle"
      .text(function(d) {
         return d.name
      })
      .on("mouseout", function(d, i) {
         d3.select(this).style("font-size", "90%");
      })
      .on("mouseover", function(d, i) {
         d3.select(this).style("font-size", "120%");
      })
      ;

      // Links
      var link = svg.selectAll(".link")
      .data(json.links)
      .enter().append("line")
      .on("mouseout", function(d, i) {
         d3.select(this).style("stroke", " #a0a0a0");
      })
      .on("mouseover", function(d, i) {
         d3.select(this).style("stroke", " #000000");

      })
      .attr("class", "link")
      ;

   
      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 + ")";
         });
      });
     
   });
}


Could anyone give me a clue, how to add Labels to the linklines?
I have spent hours in searching for something in the net, but there is only this thread and i get it not working.

Thanks in advance!

Chandra

unread,
Mar 4, 2013, 6:33:15 PM3/4/13
to d3...@googlegroups.com
I used the below in my example

ex:1
<path id="12_38" class="link2" stroke="red" d="M 310.11284782248526 221.2892019450783 L 272.5007247428536 184.34129949487706 L 234.88860166322195 147.39339704467582" style="display: inline; "></path>
<text><textpath xlink:href="#12_38">sometext: 120</textpath></text>

but unfortunately when I used the same in D3 its not redering the textpath with xlink:href, instead it renders as <textpath href..>

Some where in a blog it was mentioned to use  xlink:xlink:href instead of xlink:href. I tried , this time I can see the tag in firebug but still not rendering the text. But this should work as per ex1. 

Chris Viau

unread,
Mar 4, 2013, 6:43:45 PM3/4/13
to d3...@googlegroups.com
Try to add these attributes to your svg:

I used to double the xlink as you described. Ian filed a bug a while ago about this behavior in Webkit. But it's supposed to be fixed and to also work in Firefox.


--
You received this message because you are subscribed to the Google Groups "d3-js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Chandra

unread,
Mar 5, 2013, 8:23:45 PM3/5/13
to d3...@googlegroups.com
Chris, 
I tried with svg xmlns but no use. 

but worked after I adding .attr("xlink:xlink:href"...) in tick handler.

Thanks.

Dustin Larimer

unread,
Jul 12, 2013, 8:49:31 PM7/12/13
to d3...@googlegroups.com
Excellent -- thank you, Jason! This webkit funk was also preventing text contained in textPath elements from being visible on the initial rendering of fixed nodes/links in Safari.
Reply all
Reply to author
Forward
0 new messages