score:3

Accepted answer

I think I figured out most of the confusion from my original answer.

Creating the "zero length path" on link creation

var linkEnter = link.enter().insert('path', "g")
  .attr("class", "link")
  .attr('d', function(d){
    var o = {x: source.x0, y: source.y0}
    return diagonal(o, o)
});

and on link removal

var linkExit = link.exit().transition()
  .duration(duration)
  .attr('d', function(d) {
    var o = {x: source.x, y: source.y}
    return diagonal(o, o)
  })
 .remove();

is used to create the animation, when the nodes, together with the related links slides-out/retrieves-back-in their parent nodes. The animation animates transformation of the path to/from their final shape from/to the "null" shape - thus the link starting and finishing at the same coordinates.

The link generator can be then used like this

// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert('path', "g")
    .attr("class", "link")
    .attr('d', d3.linkHorizontal()
        .source(function(){ return [sourceNode.y0, sourceNode.x0]})
        .target(function(){ return [sourceNode.y0, sourceNode.x0]}));

 ...

// Transition back to the parent element position
linkUpdate.transition()
    .duration(duration)
    .attr('d', d3.linkHorizontal()
        .source(function (d) {return [d.parent.y, d.parent.x]})
        .target(function (d) {return [d.y, d.x]})
    );

 ...

// Remove any exiting links
var linkExit = link.exit().transition()
    .duration(duration)
    .attr('d', d3.linkHorizontal()
        .source(function(){ return [sourceNode.y, sourceNode.x]})
        .target(function(){ return [sourceNode.y, sourceNode.x]}))
    .remove();

Related Query