title: d3.js - tree node distance equally border to border
body: <p>I would like tree node distance equally from border to border, by default it's from center to center.</p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>draw_tree()
  function tree(root) {
    var width = 300
    var height = 140
    var step = 50
    var margin = {top: 0, right: 40, bottom: 0, left: 40}
  
    var treemap = d3.tree().size([height,200])
    var node = treemap(d3.hierarchy(root))
  
    var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  
    var nodes = treemap(d3.hierarchy(root))
    var g = svg.selectAll("g")
    .data([nodes])
    .enter().append("g")
    .attr("class", "nodes")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  
    var link = g.append("g")
    .attr("class", "link")
    .selectAll("path")
    .data(function(d) { return treemap(d).links(); })
    .enter().append("path")
    .attr("class", "links")
    .attr('fill','none')
    .attr('stroke','black')
  
    var node = g.append("g")
    .attr("class", "node")
    .selectAll("g")
    .data(function(d) { return nodes; })
    .enter().append("g")
    .attr("class", "nodes")
  
    node.append("rect")
      .attr('fill','none')
    
    var pos = []
    var txt = node.append("text")
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .attr("font-family", "sans-serif")
    .attr('font-size',12)
    .text(function(d) { 
      return d.data.type == 'hidden'?'':d.data.name; 
    })
    .each(function(d,i,n) { 
      d.width = Math.max(50, this.getComputedTextLength() + 10); 
      d.height=20
      if (d.data.type == 'hidden') {
        d.width = 0
        d.height = 0
      }
    })
    .attr("x",0);
  
    node.filter(function(d) { return "join" in d.data; }).insert("path", "text")
      .attr("class", "join");
  
    tick(svg);
    return svg;
  }
  
  function tick(svg) {
    var node = svg.selectAll(".node g")
    .attr("class", function(d) { return d.data.type; })
    .attr("transform", function(d, i) { return "translate("+ d.y + "," + d.x + ")"; })
  
    node.select("rect")
      .attr('stroke','black')
      .attr("ry", 0)
      .attr("rx", 0)
      .attr('x',d =&gt; -d.width/2)
      .attr("y", d =&gt; -d.height/2)
      .attr("height", 20)
      .attr("width", function(d) { return d.width; })
  
    var diagjoin = function link(d) {
      var sy = d.width
      var sx = 0
      var ty = 88
      var tx = d.data.join * 24
      return "M" + sy + "," + sx
        + "C" + (sy + ty) / 2 + "," + sx
        + " " + (sy + ty) / 2 + "," + tx
        + " " + ty + "," + tx;
    };
  
    var diagpath = function link(d) {
      var sy = d.source.y
      var sx = d.source.x
      var ty = d.target.y
      var tx = d.target.x
      return "M" + sy + "," + sx
        + "C" + (sy + ty) / 2 + "," + sx
        + " " + (sy + ty) / 2 + "," + tx
        + " " + ty + "," + tx;
    };
  
    const zagzag1 = (d) =&gt; {
      let from = d.source
      let to = d.target
      let dx = to.y - from.y;
      let dy = to.x - from.x;
      if (Math.abs(dx) &gt; Math.abs(dy)) {
        let mx,my,nx,ny,sx,sy
        sx = (dx&gt;0)?1:-1
        sy = (dy&gt;0)?1:-1
        mx = from.y + from.width/2*sx
        my = from.x
        nx = to.y - to.width/2*sx
        ny = to.x
        dx = nx - mx;
        dy = ny - my;
  
        return `M ${mx},${my} l ${dx/2},0 0,${dy} ${dx/2},0 `;
      }else{//vertical
        let mx,my,nx,ny,sx,sy
        sx = (dx&gt;0)?1:-1
        sy = (dy&gt;0)?1:-1
        mx = from.y 
        my = from.x + from.height/2*sy
        nx = to.y
        ny = to.x - to.height/2*sy
        dx = nx - mx;
        dy = ny - my;
        return `M ${mx},${my} l 0,${dy/2} ${dx},0 0,${dy/2}`; 
      }
    };
  
    const zagzag = (d) =&gt; {
      let from = d.source
      let to = d.target
      let dx = to.y - from.y;
      let dy = to.x - from.x;
      if (Math.abs(dx) &gt; Math.abs(dy)) {
        let mx,my,nx,ny,sx,sy
        sx = (dx&gt;0)?1:-1
        sy = (dy&gt;0)?1:-1
        mx = from.y + from.width/2*sx
        my = from.x
        nx = to.y - to.width/2*sx
        ny = to.x
        dx = nx - mx;
        dy = ny - my;
  
        return `M ${mx},${my} l 0,${dy} ${dx},0`;
      }else{//vertical
        let mx,my,nx,ny,sx,sy
        sx = (dx&gt;0)?1:-1
        sy = (dy&gt;0)?1:-1
        mx = from.y 
        my = from.x + from.height/2*sy
        nx = to.y
        ny = to.x - to.height/2*sy
        dx = nx - mx;
        dy = ny - my;
        return `M ${mx},${my} l ${dx},0 0,${dy}`; 
      }
    };
  
    node.select(".join")
      .attr("d",d =&gt; zagzag(d) );
  
    svg.selectAll(".link path")
      .attr("d", d =&gt; zagzag(d));
  }
  
  function draw_tree() {
    tree(
      {name:'AAAA',children:[
        {name: "AAAA",type:'hidden', children: [
          {name: "BBBB", children: [{name: "HHHH"}]},
          {name: "CCCC", children: [{name: "IIII"}]},
          {name: "DDDD", children: [{name: "JJJJ"}]},
          {name: "EEEE", children: [{name: "KKKK"}]},
          {name: "FFFF", children: [{name: "LLLL"}]},
          {name: "GGGG", children: [{name: "MMMM"}]},
        ]}
      ]}
    );
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"&gt;&lt;/script&gt;</code></pre>
  </div>
  </div>
  </p>