d3.js 在d3树中选择节点的后代并突出显示它们

sf6xfgos  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(149)

我想重现下面的图像:给定我的数据中某个数据的id,我想选择它及其后代,突出显示节点和到它们的路径。

我可以使用d3.select来识别目标节点,但是在识别它的后代节点(d3.select("#blah").descendants()返回undefined)和适当地突出显示它们以及它们的路径时遇到了麻烦。

de90aj5v

de90aj5v1#

要获取给定节点的后代,您需要在节点对象上调用descendants(),而不是在相应的d3选择上。
例如,给定这个简单的树I found online

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  /* set the CSS */

  .node circle {
    fill: #fff;
    stroke: steelblue;
    stroke-width: 3px;
  }

  .node text {
    font: 12px sans-serif;
  }

  .node--internal text {
    text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
  }

  .link {
    fill: none;
    stroke: #ccc;
    stroke-width: 2px;
  }
</style>

<body>

  <!-- load the d3.js library -->
  <script src="https://d3js.org/d3.v5.min.js"></script>
  <script>
    var treeData = {
      "name": "Top Level",
      "children": [{
          "name": "Level 2: A",
          "children": [{
              "name": "Son of A"
            },
            {
              "name": "Daughter of A"
            }
          ]
        },
        {
          "name": "Level 2: B"
        }
      ]
    };

    // set the dimensions and margins of the diagram
    var margin = {
        top: 20,
        right: 90,
        bottom: 30,
        left: 90
      },
      width = 660 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;

    // declares a tree layout and assigns the size
    var treemap = d3.tree()
      .size([height, width]);

    //  assigns the data to a hierarchy using parent-child relationships
    var nodes = d3.hierarchy(treeData, function(d) {
      return d.children;
    });

    // maps the node data to the tree layout
    nodes = treemap(nodes);

    // append the svg object to the body of the page
    // appends a 'group' element to 'svg'
    // moves the 'group' element to the top left margin
    var svg = d3.select("body").append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom),
      g = svg.append("g")
      .attr("transform",
        "translate(" + margin.left + "," + margin.top + ")");

    // adds the links between the nodes
    var link = g.selectAll(".link")
      .data(nodes.descendants().slice(1))
      .enter().append("path")
      .attr("class", "link")
      .attr("d", function(d) {
        return "M" + d.y + "," + d.x +
          "C" + (d.y + d.parent.y) / 2 + "," + d.x +
          " " + (d.y + d.parent.y) / 2 + "," + d.parent.x +
          " " + d.parent.y + "," + d.parent.x;
      });

    // adds each node as a group
    var node = g.selectAll(".node")
      .data(nodes.descendants())
      .enter().append("g")
      .attr("class", function(d) {
        return "node" +
          (d.children ? " node--internal" : " node--leaf");
      })
      .attr("transform", function(d) {
        return "translate(" + d.y + "," + d.x + ")";
      });

    // adds the circle to the node
    node.append("circle")
      .attr("r", 10);

    // adds the text to the node
    node.append("text")
      .attr("dy", ".35em")
      .attr("x", function(d) {
        return d.children ? -13 : 13;
      })
      .style("text-anchor", function(d) {
        return d.children ? "end" : "start";
      })
      .text(function(d) {
        return d.data.name;
      });
  </script>
</body>

要基于选择获取Level 2A的所有后代节点(包括其自身),可以执行以下操作:

const myNodeSelection = d3.selectAll(".node").filter(d=>d.data.name === "Level 2: A");
const descendants = myNodeSelection.datum().descendants();

下面是演示,请看一下控制台:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  /* set the CSS */

  .node circle {
    fill: #fff;
    stroke: steelblue;
    stroke-width: 3px;
  }

  .node text {
    font: 12px sans-serif;
  }

  .node--internal text {
    text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
  }

  .link {
    fill: none;
    stroke: #ccc;
    stroke-width: 2px;
  }
</style>

<body>

  <!-- load the d3.js library -->
  <script src="https://d3js.org/d3.v5.min.js"></script>
  <script>
    var treeData = {
      "name": "Top Level",
      "children": [{
          "name": "Level 2: A",
          "children": [{
              "name": "Son of A"
            },
            {
              "name": "Daughter of A"
            }
          ]
        },
        {
          "name": "Level 2: B"
        }
      ]
    };

    // set the dimensions and margins of the diagram
    var margin = {
        top: 20,
        right: 90,
        bottom: 30,
        left: 90
      },
      width = 660 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;

    // declares a tree layout and assigns the size
    var treemap = d3.tree()
      .size([height, width]);

    //  assigns the data to a hierarchy using parent-child relationships
    var nodes = d3.hierarchy(treeData, function(d) {
      return d.children;
    });

    // maps the node data to the tree layout
    nodes = treemap(nodes);

    // append the svg object to the body of the page
    // appends a 'group' element to 'svg'
    // moves the 'group' element to the top left margin
    var svg = d3.select("body").append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom),
      g = svg.append("g")
      .attr("transform",
        "translate(" + margin.left + "," + margin.top + ")");

    // adds the links between the nodes
    var link = g.selectAll(".link")
      .data(nodes.descendants().slice(1))
      .enter().append("path")
      .attr("class", "link")
      .attr("d", function(d) {
        return "M" + d.y + "," + d.x +
          "C" + (d.y + d.parent.y) / 2 + "," + d.x +
          " " + (d.y + d.parent.y) / 2 + "," + d.parent.x +
          " " + d.parent.y + "," + d.parent.x;
      });

    // adds each node as a group
    var node = g.selectAll(".node")
      .data(nodes.descendants())
      .enter().append("g")
      .attr("class", function(d) {
        return "node" +
          (d.children ? " node--internal" : " node--leaf");
      })
      .attr("transform", function(d) {
        return "translate(" + d.y + "," + d.x + ")";
      });

    // adds the circle to the node
    node.append("circle")
      .attr("r", 10);

    // adds the text to the node
    node.append("text")
      .attr("dy", ".35em")
      .attr("x", function(d) {
        return d.children ? -13 : 13;
      })
      .style("text-anchor", function(d) {
        return d.children ? "end" : "start";
      })
      .attr("id", function(d) {
        return d.data.name;
      })
      .text(function(d) {
        return d.data.name;
      });

    const myNodeSelection = d3.selectAll(".node").filter(d => d.data.name === "Level 2: A");
    const descendants = myNodeSelection.datum().descendants();
    console.log(descendants);
  </script>
</body>

同样,这也没有必要:因为您已经有了层次对象,只需使用它来获取descendants()即可。

相关问题