Enter, update and exit selections in a treemap

拥有回忆 提交于 2021-02-11 10:30:07

问题


I have a D3 v5 treemap that I show three levels of the hierarchy via a filter. Showing all levels at once would make the map too busy. I'd like to employ a drill down or drill up feature such that when you click on a given rect (either child or parent) that re-sets that rect as the root node and then displays another three levels of the hierarchy from that new root node.

I think I'm getting the new root node data, but I can't seem to get the map to update correctly with the onclick (the console.log in the exit().remove() doesn't get called after the onclick). It seems like elements are not exiting correctly, or not getting to the exit().remove().

Here is the code:

const treemapLayout = d3.treemap()
  .size([1200, 700])
  .paddingOuter(16);

d3.json("test.json").then(function(data) {

  // update the view
  const update = (d) => {
  //console.log(d)

  let rootNode = d3.hierarchy(d)

  console.log(rootNode)

  rootNode
    .sum(function(d) { return d.value; })
    .sort(function(a, b) { return b.height - a.height || b.value - a.value; });

  treemapLayout(rootNode);


  let nodes = d3.select('svg g')
    .selectAll('g')
    .data(rootNode.descendants())

  nodes
    .enter()
    .filter(function(d) {
      return d.depth < 3;
    })
    .append('g')
    .merge(nodes)
    .attr('transform', function(d) {return 'translate(' + [d.x0, d.y0] + ')'})



  //nodes
    .append('rect')
    .attr('width', function(d) { return d.x1 - d.x0; })
    .attr('height', function(d) { return d.y1 - d.y0; })
    .attr('style', function(d) {
      return ('fill:' + d3.interpolateRdYlGn(d.data.health))
    })
    .on('click', function(d.parent) { console.log(d.data.name); update(d); })

  nodes
    .append('text')
    .attr('dx', 4)
    .attr('dy', 14)
    .text(function(d) {
      return d.data.name;
    })


  nodes
    .attr('style', function(d) { console.log('here'); return d; })
    .exit().remove()
  };

  update(data);
});

Here is the data in a simplified format:

{
  "name": "A1",
  "health": 0.521,
  "children": [
    {
      "name": "B1",
      "health": 0.521,
      "children": [
        {
          "name": "B1-C1",
          "health": 0.614,
          "children": [
            {
              "name": "B1-C1-D1",
              "health": 0.666,
              "children": [
                {
                  "name": "B1-C1-D1-E1",
                  "value": 30,
                  "health": 0.8
                },
                {
                  "name": "B1-C1-D1-E2",
                  "value": 35,
                  "health": 0.5
                },
                {
                  "name": "B1-C1-D1-E3",
                  "value": 20,
                  "health": 0.7
                }
              ]
            },
            {
              "name": "B1-C1-D2",
              "health": 0.45,
              "children": [
                {
                  "name": "B1-C1-D2-E1",
                  "value": 10,
                  "health": 0.8
                },
                {
                  "name": "B1-C1-D2-E1",
                  "value": 14,
                  "health": 0.1
                }
              ]
            },
            {
              "name": "B1-C1-D3",
              "health": 0.64,
              "children": [
                {
                  "name": "B1-C1-D3-E1",
                  "value": 10,
                  "health": 0.8
                },
                {
                  "name": "B1-C1-D3-E2",
                  "value": 14,
                  "health": 0.2
                },
                {
                  "name": "B1-C1-D3-E3",
                  "value": 7,
                  "health": 0.7
                },
                {
                  "name": "B1-C1-D3-E4",
                  "value": 9,
                  "health": 0.9
                },
                {
                  "name": "B1-C1-D3-E5",
                  "value": 5,
                  "health": 0.6
                }
              ]
            },
            {
              "name": "B1-C1-D4",
              "value": 2,
              "health": 0.7
            }
          ]
        },
        {
          "name": "B1-C2",
          "health": 0.45,
          "children": [
            {
              "name": "B1-C2-D1",
              "health": 0.45,
              "value": 12
            }
          ]
        },
        {
          "name": "B1-C3",
          "health": 0.5,
          "children": [
            {
              "name": "B1-C3-D1",
              "health": 0.5,
              "value": 10
            }
          ]
        }
      ]
    }
  ]
}

回答1:


D3 selections are immutable.

When you do this...

nodes.enter()
    .filter(function(d) {
      return d.depth < 3;
    })
    .append('g')
    .merge(nodes)
    //etc...

... the merge is not changing what nodes is, which is just the update selection.

You have to reassign it:

nodes = nodes.enter()
    //etc...

Here is your code with that change (in a smaller SVG):

const data = {
  "name": "A1",
  "health": 0.521,
  "children": [{
    "name": "B1",
    "health": 0.521,
    "children": [{
        "name": "B1-C1",
        "health": 0.614,
        "children": [{
            "name": "B1-C1-D1",
            "health": 0.666,
            "children": [{
                "name": "B1-C1-D1-E1",
                "value": 30,
                "health": 0.8
              },
              {
                "name": "B1-C1-D1-E2",
                "value": 35,
                "health": 0.5
              },
              {
                "name": "B1-C1-D1-E3",
                "value": 20,
                "health": 0.7
              }
            ]
          },
          {
            "name": "B1-C1-D2",
            "health": 0.45,
            "children": [{
                "name": "B1-C1-D2-E1",
                "value": 10,
                "health": 0.8
              },
              {
                "name": "B1-C1-D2-E1",
                "value": 14,
                "health": 0.1
              }
            ]
          },
          {
            "name": "B1-C1-D3",
            "health": 0.64,
            "children": [{
                "name": "B1-C1-D3-E1",
                "value": 10,
                "health": 0.8
              },
              {
                "name": "B1-C1-D3-E2",
                "value": 14,
                "health": 0.2
              },
              {
                "name": "B1-C1-D3-E3",
                "value": 7,
                "health": 0.7
              },
              {
                "name": "B1-C1-D3-E4",
                "value": 9,
                "health": 0.9
              },
              {
                "name": "B1-C1-D3-E5",
                "value": 5,
                "health": 0.6
              }
            ]
          },
          {
            "name": "B1-C1-D4",
            "value": 2,
            "health": 0.7
          }
        ]
      },
      {
        "name": "B1-C2",
        "health": 0.45,
        "children": [{
          "name": "B1-C2-D1",
          "health": 0.45,
          "value": 12
        }]
      },
      {
        "name": "B1-C3",
        "health": 0.5,
        "children": [{
          "name": "B1-C3-D1",
          "health": 0.5,
          "value": 10
        }]
      }
    ]
  }]
}

const treemapLayout = d3.treemap()
  .size([500, 300])
  .paddingOuter(16);

// update the view
const update = (d) => {
  //console.log(d)

  let rootNode = d3.hierarchy(d)

  console.log(rootNode)

  rootNode
    .sum(function(d) {
      return d.value;
    })
    .sort(function(a, b) {
      return b.height - a.height || b.value - a.value;
    });

  treemapLayout(rootNode);


  let nodes = d3.select('svg g')
    .selectAll('g')
    .data(rootNode.descendants())

  nodes
    .exit().remove()

  nodes = nodes
    .enter()
    .filter(function(d) {
      return d.depth < 3;
    })
    .append('g')
    .merge(nodes)
    .attr('transform', function(d) {
      return 'translate(' + [d.x0, d.y0] + ')'
    })



  //nodes
  nodes.append('rect')
    .attr('width', function(d) {
      return d.x1 - d.x0;
    })
    .attr('height', function(d) {
      return d.y1 - d.y0;
    })
    .attr('style', function(d) {
      return ('fill:' + d3.interpolateRdYlGn(d.data.health))
    })
    .on('click', function(d) {
      console.log(d.data.name);
      update(d);
    })

  nodes
    .append('text')
    .attr('dx', 4)
    .attr('dy', 14)
    .text(function(d) {
      return d.data.name;
    })

};

update(data);
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="300">
  <g></g>
</svg>


来源:https://stackoverflow.com/questions/57470346/enter-update-and-exit-selections-in-a-treemap

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!