将标签放在可折叠d3树布局上的链接旁边

时间:2016-01-19 07:48:34

标签: d3.js

我正在尝试使用d3树布局创建一个双树插件,我可以在点击父级时动态添加子项。我正在尝试在每个链接或路径上添加标签。 我尝试做的是添加g元素,然后在其中添加textpath元素。但问题是我无法将文本准确地放在链接上。如何将转换应用于text,以便将其放在连接两个元素的链接旁边。

以下是相关代码 -

 var edge = d3.select(".right-tree-container")
 .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);


  edge.append("text")
 .text("test-label");

这是我的代码。对于长版本感到抱歉,无法缩短版本 - 为方便起见,我已经评论了代码的一部分,这对于上述问题非常重要。

出于演示目的,只在右侧树上添加了代码。

使用: - 单击节点,单击+符号以添加节点。单击路径以删除节点及其已连接的子节点。

 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 = 1260 - 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)");

   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) {
         if (d.parent == "null") {
           return "node rightparent" //since its root its parent is null
         } 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");




     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) {

       console.log("chevron-right");
     });




     // 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…

     /***************************************

      required code , where group edge-container edge is added 

       ************************/

     var edge = d3.select(".right-tree-container")
       .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);


     edge.append("text")
       .text("test-label");



     /***** 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) {

       console.log(d._children);
       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.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)");

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

     // Update the links…
     var link = svg.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", "g")
       .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)
       }

     }

     // 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();

     }

     console.log($(this).children("addLeftChild"));
     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%;
}
#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;
}
.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;
}
.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;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.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 class="doubletree-container">

</div>

<script type="text/javascript">
  $(document).ready(function() {

    $(".doubletree-container").makeDoubleTree();

  });
</script>

1 个答案:

答案 0 :(得分:1)

似乎您没有将x和y添加到链接标签。

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 x;          
         })
       .attr("y",function(d){
          var y = (d.source.x +d.target.x)/2          
          return y;          
         })
       .text("test-label");

还需要删除现有链接,否则会添加多个链接。所以也添加了这一行

d3.selectAll(".text-link").remove();