title: Appending multiline text to nodes in directed graphs in d3js
body: <p>I am trying to create multi line text as nodes of the directed graph as :</p>
  
  <pre><code>    &lt;rect height="27" width="56" rx="100" ry="100" style="fill: #ffffff;"&gt;&lt;/rect&gt;
      &lt;text dx="6" dy="6"&gt;
          &lt;tspan x="0" dy="15"&gt;Donovan&lt;/tspan&gt;
          &lt;tspan x="0" dy="15"&gt;3&lt;/tspan&gt;
          &lt;tspan x="0" dy="15"&gt;what&lt;/tspan&gt;
      &lt;/text&gt;
  </code></pre>
  
  <p>as seen in: <a href="http://jsfiddle.net/nikosdim/S4eaL/1/" rel="nofollow">http://jsfiddle.net/nikosdim/S4eaL/1/</a></p>
  
  <p>I currently have this:</p>
  
  <pre><code>// setting up parameters to be use through rest of the code
  var w = 2000;
  var h = 800;
  var r = 30;
  var linkDistance = 100;
  var boxHeight = 50;
  var boxWidth = 50;
  var colors = d3.scale.category10();
  
  // This is what how we should be setting gravity, theta and charge ideally: http://stackoverflow.com/questions/9901565/charge-based-on-size-d3-force-layout
  var charge = -5000;
  var gravity = 0.3;
  var theta = 0.01;
  
  var dataset = {
      nodes: [
      { "name": "Adam", "id": "0" },
      { "name": "Bob", "id": "1" },
      { "name": "Carrie", "id": "2" },
      { "name": "Donovan", "id": "3" },
      { "name": "Edward", "id": "4" },
      { "name": "Felicity", "id": "5" },
      { "name": "George", "id": "6" },
      { "name": "Hannah", "id": "7" },
      { "name": "Iris", "id": "8" },
      { "name": "Jerry", "id": "9" }
      ],
      edges: [
      { "source": 0, "target": 4 },
      { "source": 1, "target": 5 },
      { "source": 2, "target": 5 },
      { "source": 2, "target": 5 },
      { "source": 5, "target": 8 },
      { "source": 5, "target": 9 },
      { "source": 6, "target": 7 },
      { "source": 7, "target": 8 },
      { "source": 8, "target": 9 }
      ]
  };
  
  var svg = d3.select("body").append("svg").attr({ "width": w, "height": h });
  
  var force = d3.layout.force()
      .nodes(dataset.nodes)
      .links(dataset.edges)
      .size([w, h])
      .linkDistance([linkDistance])
      .charge(charge)
      .theta(theta)
      .gravity(gravity);
  
  var edges = svg.selectAll("line")
    .data(dataset.edges)
    .enter()
    .append("line")
    .attr("id", function (d, i) { return 'edge' + i; })
    .attr('marker-end', 'url(#arrowhead)')
    .style("stroke", "#ccc")
    .style("pointer-events", "none");
  
  var nodes = svg.selectAll("rect")
    .data(dataset.nodes)
    .enter()
    .append("rect")
    .attr({ "width": boxWidth })
    .attr({ "height": boxHeight })
    //.style("fill", function (d, i) { return colors(i); })
    .style("fill", 'white')
    .attr('stroke', 'black')
    .call(force.drag);
  
  var nodelabels = svg.selectAll(".nodelabel")
     .data(dataset.nodes)
     .enter()
     .append("text")
     .attr({
         //"x": function (d) { return d.x; },
         //"y": function (d) { return d.y; },
         "class": "nodelabel",
         "stroke": "black"
     });
  
  var edgepaths = svg.selectAll(".edgepath")
      .data(dataset.edges)
      .enter()
      .append('path')
      .attr({
          //'d': function (d) { return 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y; },
          'class': 'edgepath',
          'fill-opacity': 0,
          'stroke-opacity': 0,
          'fill': 'blue',
          'stroke': 'red',
          'id': function (d, i) { return 'edgepath' + i; }
      })
      .style("pointer-events", "none");
  
  var edgelabels = svg.selectAll(".edgelabel")
      .data(dataset.edges)
      .enter()
      .append('text')
      .style("pointer-events", "none")
      .attr({
          'class': 'edgelabel',
          'id': function (d, i) { return 'edgelabel' + i; },
          'dx': 80,
          'dy': 0,
          'font-size': 10,
          'fill': '#aaa'
      });
  
  edgelabels.append('textPath')
      .attr('xlink:href', function (d, i) { return '#edgepath' + i; })
      .style("pointer-events", "none")
      .text(function (d, i) { return 'label ' + i; });
  
  
  svg.append('defs').append('marker')
      .attr({
          'id': 'arrowhead',
          'viewBox': '-0 -5 10 10',
          'refX': 25,
          'refY': 0,
          //'markerUnits':'strokeWidth',
          'orient': 'auto',
          'markerWidth': 10,
          'markerHeight': 10,
          'xoverflow': 'visible'
      })
      .append('svg:path')
          .attr('d', 'M 0,-5 L 10 ,0 L 0,5')
          .attr('fill', '#ccc')
          .attr('stroke', '#ccc');
  
  
  force.on("tick", tick).start();
  
  function ConstrainX(point) {
      return Math.max(r, Math.min(w - r, point));
  }
  
  function ConstrainY(point) {
      return Math.max(r, Math.min(h - r, point));
  }
  
  function tick(e) {
      // Push sources up and targets down to form a weak tree.
      var k = 60 * e.alpha;
      dataset.edges.forEach(function (d, i) {
          d.source.y -= k;
          d.target.y += k;
      });
  
      edges.attr({
          "x1": function (d) { return ConstrainX(d.source.x); },
          "y1": function (d) { return ConstrainY(d.source.y); },
          "x2": function (d) { return ConstrainX(d.target.x); },
          "y2": function (d) { return ConstrainY(d.target.y); }
      });
  
      nodes.attr({
          "x": function (d) { return ConstrainX(d.x) - boxWidth / 2; },
          "y": function (d) { return ConstrainY(d.y) - boxHeight / 2; }
      });
  
      // appending boxWidth/2 to make sure the labels are within the box
      nodelabels.attr("x", function (d) { return ConstrainX(d.x) - boxWidth / 2; })
                .attr("y", function (d) { return ConstrainY(d.y); });
  
      edgepaths.attr('d', function (d) {
          var path = 'M ' + ConstrainX(d.source.x) + ' ' + ConstrainY(d.source.y) + ' L ' + ConstrainX(d.target.x) + ' ' + ConstrainY(d.target.y);
          //console.log(d)
          return path;
      });
  
      edgelabels.attr('transform', function (d, i) {
          if (d.target.x &lt; d.source.x) {
              bbox = this.getBBox();
              rx = bbox.x + bbox.width / 2;
              ry = bbox.y + bbox.height / 2;
              return 'rotate(180 ' + rx + ' ' + ry + ')';
          }
          else {
              return 'rotate(0)';
          }
      });
  
      var insertLinebreaks = function (d) {
          var el = d3.select(this);
          var name = d.name;
          var id = d.id;
  
          el.text('');
  
          //for (var i = 0; i &lt; words.length; i++) {
          var tspan = el.append('tspan').text(name);
          tspan = el.append('tspan').text(id);
          //if (i &gt; 0)
          tspan.attr('x', 0);
          tspan.attr('dy', '15');
          tspan = el.append('tspan').text('what');
          tspan.attr('x', '0');
          tspan.attr('dy', '15');
          //}
      };
  
      nodelabels.each(insertLinebreaks); &lt;== Insert new lines
  }
  </code></pre>
  
  <p>But this is messing new lines in the nodes.  Once I insert the new lines the text shows up left aligned at the start of the screen.  This is not what I want.  I wanted the text to be aligned in the node as shown in the first image.</p>
  
  <p>This is the output using the above code:</p>
  
  <p><a href="http://jsfiddle.net/6afc2vp8/" rel="nofollow">http://jsfiddle.net/6afc2vp8/</a></p>