在 d3 js 树中插入多次工作

Insert working multiple time in d3 js tree

本文关键字:工作 插入 d3 js      更新时间:2023-09-26

我正在创建一棵树并尝试为边缘添加标签。在同一树中,可以使用单击父节点时出现的加号按钮动态添加节点。当我添加新的孩子时出现问题。

(抱歉代码太长,无法制作更小的工作示例 - 为了便于分析,已注释代码)

问题所在

当我检查 DOM 时,我可以看到text具有类 text-link 的元素已多次添加到具有类edge-container的元素g。(请检查代码片段)。呈现的标记如下所示。(仅显示相关部分)

<g class="edge-container">
<path class="link" id="rightlink1-2" d="M0,230C90,230 90,76.66666666666667 180,76.66666666666667"></path>
<text class="text-link" x="135" y="230" text-anchor="start" style="font-size: 14px;">test-label</text>
</g>
<g class="edge-container">
<path class="link" id="rightlink1-3" d="M0,230C90,230 90,230 180,230"></path>
<text class="text-link" x="135" y="172.5" text-anchor="start" style="font-size: 14px;">test-label</text>
<text class="text-link" x="135" y="287.5" text-anchor="start" style="font-size: 14px;">test-label</text>
</g>
<g class="edge-container">
<path class="link" id="rightlink1-4" d="M0,230C90,230 90,383.33333333333337 180,383.33333333333337"></path>
<text class="text-link" x="135" y="153.33333333333334" text-anchor="start" style="font-size: 14px;">test-label</text>
<text class="text-link" x="135" y="230" text-anchor="start" style="font-size: 14px;">test-label</text>
<text class="text-link" x="135" y="306.6666666666667" text-anchor="start" style="font-size: 14px;">test-label</text>
</g>

我做错了什么?

也就是说,对于每个新节点的添加,所有以前的标签也会附加到新创建的edge-container上。

应该对代码进行哪些更改,对于每个边缘,只有相应的text-link添加到父g元素中。

操作

单击节点以显示添加按钮

,单击添加按钮以添加子节点。(请全屏查看代码片段以方便使用)

var treeData = [{
    "name": "Device",
    "parent": "null"
  }
];
var treeData2 = [{
    "name": "Device",
    "parent": "null"
  }
];
$(document).ready(function($) {
  var margin = {
      top: 20,
      right: 120,
      bottom: 20,
      left: 120
    },
    width = 2260 - margin.right - margin.left,
    height = 500 - margin.top - margin.bottom;
  var svg = d3.select('.doubletree-container').append("svg")
    .attr("width", width + margin.right + margin.left)
    .attr("height", height + margin.top + margin.bottom);
  $.fn.makeDoubleTree = function() {
    this.makeRightTree();
    this.makeLeftTree();
  };
}(jQuery));
$.fn.makeRightTree = function() {
  // ************** Generate the tree diagram  *****************
  var margin = {
      top: 20,
      right: 120,
      bottom: 20,
      left: 120
    },
    width = 1260 - margin.right - margin.left,
    height = 500 - margin.top - margin.bottom;
  var i = 0,
    duration = 750,
    root;
  var tree = d3.layout.tree()
    .size([height, width]);
  var diagonal = d3.svg.diagonal()
    .projection(function(d) {
      return [d.y, d.x];
    });
  var svg = d3.select("svg").append("g")
    .attr("class", "right-tree-container")
    .attr("transform", "translate(600,0)");
  svg.append("g").attr("id", "right-edges")
  root = treeData[0];
  oldrx = root.x0 = height / 2;
  oldry = root.y0 = 0;
  update(root);
  function update(source) {
    // Compute the new tree layout.
    var nodes = tree.nodes(root).reverse(),
      links = tree.links(nodes);
    // Normalize for fixed-depth.
    nodes.forEach(function(d) {
      d.y = d.depth * 180;
    });
    // Update the nodes…
    var node = svg.selectAll("g.node")
      .data(nodes, function(d) {
        return d.id || (d.id = ++i);
      });
    // Enter any new nodes at the parent's previous position.
    var nodeEnter = node.enter().append("g")
      .attr("class", function(d) {
        console.log(d);
        if (d.parent == "null") {
          if (d.children)
          {
            return "node rightparent collapsed" //since its root its parent is null
          } else {
            return "node rightparent"
          }
        } else {
          if (d.children) {
            return "node rightchild collapsed" //all nodes with parent will have this class
          } else {
            return "node rightchild" //all nodes with parent will have this class
          }
        }
      })
      .attr("transform", function(d) {
        return "translate(" + source.y0 + "," + source.x0 + ")";
      })
      .on("click", click);
    nodeEnter.append("rect")
      .attr("id", function(d) {
        return "rightnode" + d.id;
      })
      .attr("x", "-10")
      .attr("y", "-15")
      .attr("height", 30)
      .attr("width", 100)
      .attr("rx", 15)
      .attr("ry", 15)
      .style("fill", "#f1f1f1");
    nodeEnter.append("image")
      .attr("xlink:href", "img.png")
      .attr("x", "0")
      .attr("y", "-10")
      .attr("width", 16)
      .attr("height", 16);

    nodeEnter.append("text")
      .attr("x", function(d) {
        return d.children || d._children ? -13 : 13;
      })
      .attr("dy", ".35em")
      .attr("text-anchor", function(d) {
        return d.children || d._children ? "end" : "start";
      })
      .text(function(d) {
        return d.name;
      })
      .style("fill-opacity", 1e-6);

    var addRightChild = nodeEnter.append("g").attr("class", "addRightChild");
    addRightChild.append("rect")
      .attr("x", "90")
      .attr("y", "-10")
      .attr("height", 20)
      .attr("width", 20)
      .attr("rx", 10)
      .attr("ry", 10)
      .style("stroke", "#444")
      .style("stroke-width", "2")
      .style("fill", "#ccc");
    addRightChild.append("line")
      .attr("x1", 95)
      .attr("y1", 1)
      .attr("x2", 105)
      .attr("y2", 1)
      .attr("stroke", "#444")
      .style("stroke-width", "2");
    addRightChild.append("line")
      .attr("x1", 100)
      .attr("y1", -4)
      .attr("x2", 100)
      .attr("y2", 6)
      .attr("stroke", "#444")
      .style("stroke-width", "2");
    // adding the right chevron
    var rightChevron = nodeEnter.append("g").attr("class", "right-chevron");

    rightChevron.append("line")
      .attr("x1", 75)
      .attr("y1", -5)
      .attr("x2", 80)
      .attr("y2", 0)
      .attr("stroke", "#444")
      .style("stroke-width", "2");
    rightChevron.append("line")
      .attr("x1", 80)
      .attr("y1", 0)
      .attr("x2", 75)
      .attr("y2", 5)
      .attr("stroke", "#444")
      .style("stroke-width", "2");
    rightChevron.on("click", function(d) {
    });

    // Transition nodes to their new position.
    var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) {
        if (d.parent == "null") {
          d.y = oldry;
          d.x = oldrx;
        }
        return "translate(" + d.y + "," + d.x + ")";
      });
    nodeUpdate.select("text")
      .style("fill-opacity", 1);
    // Transition exiting nodes to the parent's new position.
    var nodeExit = node.exit().transition()
      .duration(duration)
      .attr("transform", function(d) {
        return "translate(" + source.y + "," + source.x + ")";
      })
      .remove();
    nodeExit.select("text")
      .style("fill-opacity", 1e-6);

    // Update the links…
    /***************************************
     start of relevant code , where group edge-container edge is added 
      ************************/
    var edge = d3.select("#right-edges")
      .append("g")
      .attr("class", "edge-container")
      .data(links);
    var link = edge.selectAll("path.link")
      .data(links, function(d) {
        return d.target.id;
      });

    // Enter any new links at the parent's previous position.
    link.enter().insert("path", "edge-container")
      .attr("class", "link")
      .attr("id", function(d) {
        return ("rightlink" + d.source.id + "-" + d.target.id)
      })
      .attr("d", function(d) {
        var o = {
          x: source.x0,
          y: source.y0
        };
        return diagonal({
          source: o,
          target: o
        });
      }).on("click", removelink)
      .on("mouseover", showRemoveButton)
      .on("mouseout", hideRemoveButton);
    function showRemoveButton() {
      console.log("hover");
    }
    function hideRemoveButton() {
      console.log("hover-out");
    }

    var text = edge.selectAll("text.text-link")
      .data(links, function(d) {
        return d.target.id + d.source.id;
      });
    text.enter().insert("text", "edge-container")
      .attr("class", "text-link")
    .attr("x", function(d) {
        var x = (d.source.y + d.target.y) / 2
        return parseInt(x + 45);
      })
      .attr("y", function(d) {
        var y = (d.source.x + d.target.x) / 2
        return y;
      })
      .text("test-label")
      .attr("text-anchor", "start")
      .style("font-size", "14px");
    var link = edge.selectAll("path.link")
      .data(links, function(d) {
        return d.target.id;
      });

    // Enter any new links at the parent's previous position.
    link.enter().insert("path", "edge-container")
      .attr("class", "link")
      .attr("id", function(d) {
        return ("rightlink" + d.source.id + "-" + d.target.id)
      })
      .attr("d", function(d) {
        var o = {
          x: source.x0,
          y: source.y0
        };
        return diagonal({
          source: o,
          target: o
        });
      }).on("click", removelink)
      .on("mouseover", showRemoveButton)
      .on("mouseout", hideRemoveButton);
    function showRemoveButton() {
      console.log("hover");
    }
    function hideRemoveButton() {
      console.log("hover-out");
    }

    /***** end of relevant code *******/
    function removelink(d) {
      var confirmDelete = confirm("Are you sure you want to delete?");
      if (confirmDelete) {
        //this is the links target node which you want to remove
        var target = d.target;
        //make new set of children
        var children = [];
        //iterate through the children 
        target.parent.children.forEach(function(child) {
          if (child.id != target.id) {
            //add to the child list if target id is not same 
            //so that the node target is removed.
            children.push(child);
          }
        });
        //set the target parent with new set of children sans the one which is removed
        target.parent.children = children;
        //redraw the parent since one of its children is removed
        update(d.target.parent)
      }
    }
    var link = svg.selectAll("path.link")
      .data(links, function(d) {
        return d.target.id;
      });
    // Transition links to their new position.
    link.transition()
      .duration(duration)
      .attr("d", diagonal);
    // Transition exiting nodes to the parent's new position.
    link.exit().transition()
      .duration(duration)
      .attr("d", function(d) {
        var o = {
          x: source.x,
          y: source.y
        };
        return diagonal({
          source: o,
          target: o
        });
      })
      .remove();
    // Stash the old positions for transition.
    nodes.forEach(function(d) {
      d.x0 = d.x;
      d.y0 = d.y;
    });
    addRightChild.on("click", function(d) {
      event.stopPropagation();
      $("#child-info").show();
      $("#child-text").val("");
      $("#btn-add-child").off('click');
      $("#btn-add-child").click(function() {
        var childname = $("#child-text").val();
        if (typeof d._children === 'undefined' || d._children === null) {
          if (typeof d.children === 'undefined') {
            var newChild = [{
              "name": childname,
              "parent": "Son Of A",
            }];
            var newnodes = tree.nodes(newChild);
            d.children = newnodes[0];
            // console.log(d.children);
            update(d);
          } else {
            var newChild = {
              "name": childname,
              "parent": "Son Of A",
            };
            // console.log(d.children);
            d.children.push(newChild);
            // console.log(d.children);
            update(d);
          }
        } else {
          var newChild = {
            "name": childname,
            "parent": "Son Of A",
          };
          d.children = d._children;
          d.children.push(newChild);
          // console.log(d.children);
          update(d);
        }
        $("#child-info").hide();
      });
    });;
  }
  // Toggle children on click.
  function click(d) {
    if (d.children) {
      d._children = d.children;
      d.children = null;
    } else {
      d.children = d._children;
      d._children = null;
    }
    update(d);
    $(".addLeftChild, .addRightChild").hide();
    if (d.id === 1) {
      $(".rightparent").children(".addRightChild").show();
      $(this).children(".addRightChild").show();
    } else {
      $(this).children(".addRightChild").show();
    }
    d3.selectAll("rect").style("fill", "#f1f1f1"); //reset all node colors
    d3.selectAll("path").style("stroke", "#85e0e0"); //reset the color for all links
    while (d.parent) {
      d3.select("#leftnode1").style("fill", "#F7CA18");
      d3.selectAll("#rightnode" + d.id).style("fill", "#F7CA18"); //color the node
      if (d.parent != "null")
        d3.selectAll("#rightlink" + d.parent.id + "-" + d.id).style("stroke", "#F7CA18"); //color the path
      d = d.parent;
    }
  }
}
$.fn.makeLeftTree = function() {
  // ************** Generate the tree diagram  *****************
  var margin = {
      top: 20,
      right: 120,
      bottom: 20,
      left: 120
    },
    width = 1260 - margin.right - margin.left,
    height = 500 - margin.top - margin.bottom;
  var i = 0,
    duration = 750,
    root;
  var tree = d3.layout.tree()
    .size([height, width]);
  var diagonal = d3.svg.diagonal()
    .projection(function(d) {
      return [d.y, d.x];
    });
  var svg = d3.select("svg").append("g")
    .attr("class", "left-tree-container")
    .attr("transform", "translate(-421,0)");
  svg.append("g").attr("id", "left-edges")
  root = treeData2[0];
  oldlx = root.x0 = height / 2;
  oldly = root.y0 = width;
  update(root);
  function update(source) {
    // Compute the new tree layout.
    var nodes = tree.nodes(root).reverse(),
      links = tree.links(nodes);
    // Normalize for fixed-depth.
    nodes.forEach(function(d) {
      d.y = width - (d.depth * 180);
    });
    // Update the nodes…
    var node = svg.selectAll("g.node")
      .data(nodes, function(d) {
        return d.id || (d.id = ++i);
      });
    // Enter any new nodes at the parent's previous position.
    var nodeEnter = node.enter().append("g")
      .attr("class", function(d) {
        if (d.parent == "null") {
          return "node leftparent" //since its root its parent is null
        } else
          return "node leftchild" //all nodes with parent will have this class
      })
      .attr("transform", function(d) {
        return "translate(" + source.y0 + "," + source.x0 + ")";
      })
      .on("click", click);
    nodeEnter.append("rect")
      .attr("x", "-10")
      .attr("id", function(d) {
        return "leftnode" + d.id;
      })
      .attr("y", "-15")
      .attr("height", 30)
      .attr("width", 100)
      .attr("rx", 15)
      .attr("ry", 15)
      .style("fill", "#f1f1f1");
    nodeEnter.append("image")
      .attr("xlink:href", "img.png")
      .attr("x", "60")
      .attr("y", "-10")
      .attr("width", 16)
      .attr("height", 16);
    nodeEnter.append("text")
      .attr("x", function(d) {
        return d.children || d._children ? -13 : 13;
      })
      .attr("dy", ".35em")
      .attr("text-anchor", function(d) {
        return d.children || d._children ? "end" : "start";
      })
      .text(function(d) {
        return d.name;
      })
      .style("fill-opacity", 1e-6);
    var addLeftChild = nodeEnter.append("g").attr("class", "addLeftChild");
    addLeftChild.append("rect")
      .attr("x", "-30")
      .attr("y", "-10")
      .attr("height", 20)
      .attr("width", 20)
      .attr("rx", 10)
      .attr("ry", 10)
      .style("stroke", "#444")
      .style("stroke-width", "2")
      .style("fill", "#ccc");
    addLeftChild.append("line")
      .attr("x1", -25)
      .attr("y1", 1)
      .attr("x2", -15)
      .attr("y2", 1)
      .attr("stroke", "#444")
      .style("stroke-width", "2");
    addLeftChild.append("line")
      .attr("x1", -20)
      .attr("y1", -4)
      .attr("x2", -20)
      .attr("y2", 6)
      .attr("stroke", "#444")
      .style("stroke-width", "2");
    var leftChevron = nodeEnter.append("g").attr("class", "left-chevron");
    leftChevron.append("line")
      .attr("x1", 5)
      .attr("y1", -5)
      .attr("x2", 0)
      .attr("y2", 0)
      .attr("stroke", "#444")
      .style("stroke-width", "2");
    leftChevron.append("line")
      .attr("x1", 0)
      .attr("y1", 0)
      .attr("x2", 5)
      .attr("y2", 5)
      .attr("stroke", "#444")
      .style("stroke-width", "2");

    // Transition nodes to their new position.
    var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) {
        if (d.parent == "null") {
          d.y = oldly;
          d.x = oldlx;
        }
        return "translate(" + d.y + "," + d.x + ")";
      });
    nodeUpdate.select("text")
      .style("fill-opacity", 1);
    // Transition exiting nodes to the parent's new position.
    var nodeExit = node.exit().transition()
      .duration(duration)
      .attr("transform", function(d) {
        return "translate(" + source.y + "," + source.x + ")";
      })
      .remove();
    // nodeExit.select("circle")
    //     .attr("r", 1e-6);
    nodeExit.select("text")
      .style("fill-opacity", 1e-6);
    var edge = d3.select("#left-edges")
      .append("g")
      .attr("class", "edge-container")
      .data(links);
    d3.selectAll(".left-tree-container .text-link").remove();
    var text = edge.selectAll(".text-link")
      .data(links, function(d) {
        return d.target.id + d.source.id;
      });
    text.enter().append("text")
      .attr("class", "text-link")
    .attr("x", function(d) {
        var x = (d.source.y + d.target.y) / 2
        return parseInt(x + 45);
      })
      .attr("y", function(d) {
        var y = (d.source.x + d.target.x) / 2
        return y;
      })
      .text("test-label")
      .attr("text-anchor", "middle")
      .style("font-size", "14px");
    var link = edge.selectAll("path.link")
      .data(links, function(d) {
        return d.target.id;
      });
    // Update the links…
    // Enter any new links at the parent's previous position.
    link.enter().insert("path", "edge-container")
      .attr("class", "link")
      .attr("id", function(d) {
        return ("leftlink" + d.source.id + "-" + d.target.id)
      })
      .attr("d", function(d) {
        var o = {
          x: source.x0,
          y: source.y0
        };
        return diagonal({
          source: o,
          target: o
        });
      }).on("click", removelink);
    function removelink(d) {
      var confirmDelete = confirm("Are you sure you want to delete?");
      if (confirmDelete) {
        //this is the links target node which you want to remove
        var target = d.target;
        //make new set of children
        var children = [];
        //iterate through the children 
        target.parent.children.forEach(function(child) {
          if (child.id != target.id) {
            //add to teh child list if target id is not same 
            //so that the node target is removed.
            children.push(child);
          }
        });
        //set the target parent with new set of children sans the one which is removed
        target.parent.children = children;
        //redraw the parent since one of its children is removed
        update(d.target.parent)
      }
    }
    var link = svg.selectAll("path.link")
      .data(links, function(d) {
        return d.target.id;
      });
    // Transition links to their new position.
    link.transition()
      .duration(duration)
      .attr("d", diagonal);
    // Transition exiting nodes to the parent's new position.
    link.exit().transition()
      .duration(duration)
      .attr("d", function(d) {
        var o = {
          x: source.x,
          y: source.y
        };
        return diagonal({
          source: o,
          target: o
        });
      })
      .remove();
    // Stash the old positions for transition.
    nodes.forEach(function(d) {
      d.x0 = d.x;
      d.y0 = d.y;
    });
    addLeftChild.on("click", function(d) {
      event.stopPropagation();
      $("#child-info").show();
      $("#child-text").val("");
      $("#btn-add-child").off('click');
      $("#btn-add-child").click(function() {
        var childname = $("#child-text").val();
        if (typeof d._children === 'undefined' || d._children === null) {
          if (typeof d.children === 'undefined') {
            var newChild = [{
              "name": childname,
              "parent": "Son Of A",
            }];
            // console.log(tree.nodes(newChild[0]));
            var newnodes = tree.nodes(newChild);
            d.children = newnodes[0];
            // console.log(d.children);
            update(d);
          } else {
            var newChild = {
              "name": childname,
              "parent": "Son Of A",
            };
            // console.log(d.children);
            d.children.push(newChild);
            // console.log(d.children);
            update(d);
          }
        } else {
          var newChild = {
            "name": childname,
            "parent": "Son Of A",
          };
          console.log("collapsed case");
          d.children = d._children;
          d.children.push(newChild);
          // console.log(d.children);
          update(d);
        }
        $("#child-info").hide();
      });
    });
  }
  // Toggle children on click.
  function click(d) {
    if (d.id !== 1) {
      if (d.children) {
        d._children = d.children;
        d.children = null;
      } else {
        d.children = d._children;
        d._children = null;
      }
      update(d);
    }
    $(".addLeftChild, .addRightChild").hide();
    if (d.id === 1) {
      $(".rightparent").children(".addRightChild").show();
      $(this).children(".addLeftChild").show();
    } else {
      $(this).children(".addLeftChild").show();
    }
    d3.selectAll("rect").style("fill", "#f1f1f1"); //reset all node colors
    d3.selectAll("path").style("stroke", "#85e0e0"); //reset the color for all links
    while (d.parent) {
      d3.selectAll("#leftnode" + d.id).style("fill", "#F7CA18"); //color the node
      if (d.parent != "null")
        d3.selectAll("#leftlink" + d.parent.id + "-" + d.id).style("stroke", "F7CA18"); //color the path
      d = d.parent;
    }
  }
}
body {
  font-family: 'Roboto', sans-serif;
  margin: 0;
  padding: 0;
}
#child-info {
  width: 100px;
  height: 50px;
  height: auto;
  position: fixed;
  padding: 10px;
  display: none;
  left: 40%;
  z-index: 100;
}
#btn-add-child {} .control-bar {
  min-height: 50px;
  padding: 10px 0px;
  background: #f1f1f1;
  border-bottom: 1px solid #ccc;
  box-shadow: 0 3px 2px -2px rgba(200, 200, 200, 0.2);
  color: #666;
  position: relative;
}
.asset-title {
  padding: 15px;
  float: left;
}
.control-buttons {
  float: right;
  padding: 10px;
}
.control-buttons button {
  border: none;
  border-radius: 0px;
  background: #fff;
  color: #666;
  height: 30px;
  width: 30px;
}
.image-viewer {
  position: fixed;
  height: 100px;
  width: 150px;
  left: 50px;
  bottom: 50px;
  border: 1px solid #ccc;
  z-index: 100;
  background: white;
}
.node {
  cursor: pointer;
}
.node text {
  font: 12px sans-serif;
}
.link {
  fill: none;
  stroke: #85e0e0;
  stroke-width: 2px;
}
.rightparent>rect {
  display: none;
}
.leftparent>rect {
  fill: #f1f1f1;
  stroke: #ccc;
  stroke-width: 2;
}
.leftparent .left-chevron {
  display: none;
}
.leftparent image {
  display: none;
}
.addLeftChild,
.addRightChild {
  display: none;
}
.left-chevron,
.right-chevron {
  display: none;
}
.collapsed .left-chevron,
.collapsed .right-chevron {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="child-info" style="display:none">
  <input type="text" id="child-text" placeholder="child name">
  <button id="btn-add-child">add</button>
</div>
<div id="panzoom" class="doubletree-container">
</div>
<script>
  $(document).ready(function() {
    $(".doubletree-container").makeDoubleTree();

  });
</script>

您忘记像左侧那样删除旧的文本链接,然后添加您应该添加的这一行:

 d3.selectAll(".right-tree-container .text-link").remove();

在你的makeRightTree函数中,如下所示:

 $.fn.makeRightTree = function() {
  // ************** Generate the tree diagram  *****************
  var margin = {
      top: 20,
      right: 120,
      bottom: 20,
      left: 120
    },
    width = 1260 - margin.right - margin.left,
    height = 500 - margin.top - margin.bottom;
  var i = 0,
    duration = 750,
    root;
  var tree = d3.layout.tree()
    .size([height, width]);
  var diagonal = d3.svg.diagonal()
    .projection(function(d) {
      return [d.y, d.x];
    });

  var svg = d3.select("svg").append("g")
    .attr("class", "right-tree-container")
    .attr("transform", "translate(600,0)");
  svg.append("g").attr("id", "right-edges")

  root = treeData[0];
  oldrx = root.x0 = height / 2;
  oldry = root.y0 = 0;
  update(root);

  function update(source) {
    // Compute the new tree layout.
    var nodes = tree.nodes(root).reverse(),
      links = tree.links(nodes);
    // Normalize for fixed-depth.
    nodes.forEach(function(d) {
      d.y = d.depth * 180;
    });
    // Update the nodes…
    var node = svg.selectAll("g.node")
      .data(nodes, function(d) {
        return d.id || (d.id = ++i);
      });
    // Enter any new nodes at the parent's previous position.
    var nodeEnter = node.enter().append("g")
      .attr("class", function(d) {
        console.log(d);
        if (d.parent == "null") {
          if (d.children)
          {
            return "node rightparent collapsed" //since its root its parent is null
          } else {
            return "node rightparent"
          }
        } else {
          if (d.children) {
            return "node rightchild collapsed" //all nodes with parent will have this class
          } else {
            return "node rightchild" //all nodes with parent will have this class
          }
        }
      })
      .attr("transform", function(d) {
        return "translate(" + source.y0 + "," + source.x0 + ")";
      })
      .on("click", click);
    nodeEnter.append("rect")
      .attr("id", function(d) {
        return "rightnode" + d.id;
      })
      .attr("x", "-10")
      .attr("y", "-15")
      .attr("height", 30)
      .attr("width", 100)
      .attr("rx", 15)
      .attr("ry", 15)
      .style("fill", "#f1f1f1");
    nodeEnter.append("image")
      .attr("xlink:href", "img.png")
      .attr("x", "0")
      .attr("y", "-10")
      .attr("width", 16)
      .attr("height", 16);


    nodeEnter.append("text")
      .attr("x", function(d) {
        return d.children || d._children ? -13 : 13;
      })
      .attr("dy", ".35em")
      .attr("text-anchor", function(d) {
        return d.children || d._children ? "end" : "start";
      })
      .text(function(d) {
        return d.name;
      })
      .style("fill-opacity", 1e-6);


    var addRightChild = nodeEnter.append("g").attr("class", "addRightChild");
    addRightChild.append("rect")
      .attr("x", "90")
      .attr("y", "-10")
      .attr("height", 20)
      .attr("width", 20)
      .attr("rx", 10)
      .attr("ry", 10)
      .style("stroke", "#444")
      .style("stroke-width", "2")
      .style("fill", "#ccc");
    addRightChild.append("line")
      .attr("x1", 95)
      .attr("y1", 1)
      .attr("x2", 105)
      .attr("y2", 1)
      .attr("stroke", "#444")
      .style("stroke-width", "2");
    addRightChild.append("line")
      .attr("x1", 100)
      .attr("y1", -4)
      .attr("x2", 100)
      .attr("y2", 6)
      .attr("stroke", "#444")
      .style("stroke-width", "2");

    // adding the right chevron
    var rightChevron = nodeEnter.append("g").attr("class", "right-chevron");


    rightChevron.append("line")
      .attr("x1", 75)
      .attr("y1", -5)
      .attr("x2", 80)
      .attr("y2", 0)
      .attr("stroke", "#444")
      .style("stroke-width", "2");
    rightChevron.append("line")
      .attr("x1", 80)
      .attr("y1", 0)
      .attr("x2", 75)
      .attr("y2", 5)
      .attr("stroke", "#444")
      .style("stroke-width", "2");
    rightChevron.on("click", function(d) {

    });


    // Transition nodes to their new position.
    var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) {
        if (d.parent == "null") {
          d.y = oldry;
          d.x = oldrx;

        }
        return "translate(" + d.y + "," + d.x + ")";
      });

    nodeUpdate.select("text")
      .style("fill-opacity", 1);
    // Transition exiting nodes to the parent's new position.
    var nodeExit = node.exit().transition()
      .duration(duration)
      .attr("transform", function(d) {
        return "translate(" + source.y + "," + source.x + ")";
      })
      .remove();
    nodeExit.select("text")
      .style("fill-opacity", 1e-6);
    var edge = d3.select("#right-edges")
      .append("g")
      .attr("class", "edge-container")
      .data(links);
 **d3.selectAll(".right-tree-container .text-link").remove();**
    var text = edge.selectAll("text.text-link")
      .data(links, function(d) {
        return d.target.id + d.source.id;
      });
    text.enter().insert("text", "edge-container")
      .attr("class", "text-link")
    .attr("x", function(d) {
        var x = (d.source.y + d.target.y) / 2
        return parseInt(x + 45);
      })
      .attr("y", function(d) {
        var y = (d.source.x + d.target.x) / 2
        return y;
      })
      .text("test-label")
      .attr("text-anchor", "start")
      .style("font-size", "14px");
    var link = edge.selectAll("path.link")
      .data(links, function(d) {
        return d.target.id;
      });


    // Enter any new links at the parent's previous position.
    link.enter().insert("path", "edge-container")
      .attr("class", "link")
      .attr("id", function(d) {
        return ("rightlink" + d.source.id + "-" + d.target.id)
      })
      .attr("d", function(d) {
        var o = {
          x: source.x0,
          y: source.y0
        };
        return diagonal({
          source: o,
          target: o
        });
      }).on("click", removelink)
      .on("mouseover", showRemoveButton)
      .on("mouseout", hideRemoveButton);
    function showRemoveButton() {
      console.log("hover");
    }
    function hideRemoveButton() {
      console.log("hover-out");
    }


    /***** end of relevant code *******/

    function removelink(d) {
      var confirmDelete = confirm("Are you sure you want to delete?");

      if (confirmDelete) {
        //this is the links target node which you want to remove
        var target = d.target;
        //make new set of children
        var children = [];
        //iterate through the children 
        target.parent.children.forEach(function(child) {
          if (child.id != target.id) {
            //add to the child list if target id is not same 
            //so that the node target is removed.
            children.push(child);
          }
        });
        //set the target parent with new set of children sans the one which is removed
        target.parent.children = children;
        //redraw the parent since one of its children is removed
        update(d.target.parent)
      }

    }
    var link = svg.selectAll("path.link")
      .data(links, function(d) {
        return d.target.id;
      });
    // Transition links to their new position.
    link.transition()
      .duration(duration)
      .attr("d", diagonal);
    // Transition exiting nodes to the parent's new position.
    link.exit().transition()
      .duration(duration)
      .attr("d", function(d) {
        var o = {
          x: source.x,
          y: source.y
        };
        return diagonal({
          source: o,
          target: o
        });
      })
      .remove();
    // Stash the old positions for transition.
    nodes.forEach(function(d) {

      d.x0 = d.x;
      d.y0 = d.y;
    });

    addRightChild.on("click", function(d) {

      event.stopPropagation();
      $("#child-info").show();
      $("#child-text").val("");
      $("#btn-add-child").off('click');
      $("#btn-add-child").click(function() {
        var childname = $("#child-text").val();

        if (typeof d._children === 'undefined' || d._children === null) {
          if (typeof d.children === 'undefined') {

            var newChild = [{
              "name": childname,
              "parent": "Son Of A",
            }];

            var newnodes = tree.nodes(newChild);
            d.children = newnodes[0];
            // console.log(d.children);
            update(d);

          } else {
            var newChild = {
              "name": childname,
              "parent": "Son Of A",
            };
            // console.log(d.children);
            d.children.push(newChild);
            // console.log(d.children);
            update(d);
          }
        } else {
          var newChild = {
            "name": childname,
            "parent": "Son Of A",
          };

          d.children = d._children;
          d.children.push(newChild);
          // console.log(d.children);
          update(d);

        }

        $("#child-info").hide();
      });
    });;
  }

  // Toggle children on click.
  function click(d) {
    if (d.children) {
      d._children = d.children;
      d.children = null;
    } else {
      d.children = d._children;
      d._children = null;
    }
    update(d);
    $(".addLeftChild, .addRightChild").hide();
    if (d.id === 1) {
      $(".rightparent").children(".addRightChild").show();
      $(this).children(".addRightChild").show();
    } else {
      $(this).children(".addRightChild").show();
    }
    d3.selectAll("rect").style("fill", "#f1f1f1"); //reset all node colors
    d3.selectAll("path").style("stroke", "#85e0e0"); //reset the color for all links
    while (d.parent) {
      d3.select("#leftnode1").style("fill", "#F7CA18");
      d3.selectAll("#rightnode" + d.id).style("fill", "#F7CA18"); //color the node
      if (d.parent != "null")
        d3.selectAll("#rightlink" + d.parent.id + "-" + d.id).style("stroke", "#F7CA18"); //color the path
      d = d.parent;
    }
  }
}

这是一个工作演示

通过添加以下代码解决了问题:

 /* transition labels to new positions */ 
         var label = svg.selectAll("text.text-link")
            .data(links, function(d) {
                return d.target.id;
            });

        label.transition()
            .duration(duration)
            .attr("d", diagonal);

        label.exit().transition()
            .duration(duration)
            .attr("d", function(d) {
                var o = {
                    x: source.x,
                    y: source.y
                };
                return diagonal({
                    source: o,
                    target: o
                });
            })
            .remove();