D3 - 使 children 填充 parent 组

D3 - Make children fill parent group

我正在尝试为 D3 中的 single-nested 数据创建一个简单的矩形结构。我希望结果如下图所示:

换句话说,每组项目的大小应使所有组占用相同的 space。

JSFiddle that I have made 没有产生正确的结果:

var item_groups_enter = item_groups.enter()
    .append("g")
    .classed("item-group", true)
    .attr("transform", function (d, i) {
    return ("translate(0, " + 50 * i + ")"); // !!! THIS NEEDS TO CHANGE !!!
});

// Append a rectangle for each item
item_groups_enter.append("rect")
    .attr("width", main_group_width)
    .attr("height", 50) // !!! THIS NEEDS TO CHANGE !!!
.attr("fill", function (d, i) {
    return colours(i)
});

// Also append a label for each item
item_groups_enter.append("text")
    .text(function (d) {
    return (d)
})
    .attr("x", main_group_width * 0.5)
    .attr("y", 25) // !!! THIS NEEDS TO CHANGE !!!
.style("text-anchor", "middle");

我意识到我需要以某种方式将 main-group 的数据(具体来说,children 的数量)传递给 item_group,但我不确定这该怎么做。我确实尝试在 main_group 上设置自定义属性 childCount,但是引用 parent 节点(然后是 grandparent 节点)的代码变得相当混乱。

有什么方法可以做到这一点?我不确定我是否应该根据 D3 或 CSS?

来考虑解决方案

当您使用带有 selection.attr 的函数时,this 设置为当前 DOM 元素。您可以使用它来访问父选择并通过它访问基础数据:

例如,

var item_groups_enter = item_groups.enter()
    .append("g")
    .classed("item-group", true)
    .attr("transform", function (d, i) {
        // this is the g node
        var parentdatum = d3.select(this.parentNode).datum();
        var itemY = available_height/parentdatum.values.length * i;

        return ("translate(0, " + itemY + ")");
    });

    var data = [{
        group: "Fruits",
        values: ["Apple", "Banana", "Pear", "Plum"]
    }, {
        group: "Cakes",
        values: ["Chocolate Cake", "Red Velvet Cake", "Carrot Cake"]
    }, {
        group: "Dogs",
        values: ["Spaniel", "Chow", "Dachshund", "Bulldog", "Beagle", "Boxer", "Pug"]
    }]

    // Get a handle on the svg HTML element
    var svg = d3.select("body").append("svg")
        .attr("width", 500)
        .attr("height", 500)

    // Calculate spacing
    var available_width = parseInt(svg.style("width"));
    var available_height = parseInt(svg.style("height"));
    var main_group_width = available_width / data.length;

    // Define the colours to use
    var colours = d3.scale.category10();

    // Make an HTML group for each of the groups in the data
    var main_groups = svg.selectAll("g")
        .data(data);

    // For each datum entered, append a new HTML group
    main_groups.enter()
        .append("g")
        .classed("main-group", true)
        .attr("transform", function (d, i) {
        return ("translate(" + i * main_group_width + ", 0)");
    })

    // Append a new group, an "item group" for each of the values in each of the main groups
    var item_groups = main_groups.selectAll("g")
        .data(function (d) {
        return (d.values)
    });

    var item_groups_enter = item_groups.enter()
        .append("g")
        .classed("item-group", true)
        .attr("transform", function (d, i) {
            var parentdatum = d3.select(this.parentNode).datum();
        return ("translate(0, " + available_height/parentdatum.values.length * i + ")");
    });

    // Append a rectangle for each item
    item_groups_enter.append("rect")
        .attr("width", main_group_width)
        .attr("height", function() {
            // we want the grand parent node
         var parentdatum = d3.select(this.parentNode.parentNode).datum();
      return available_height/parentdatum.values.length;
     }) 
        .attr("fill", function (d, i) {
            return colours(i)
        });

    // Also append a label for each item
    item_groups_enter.append("text")
        .text(function (d) {
            return (d)
        })
        .attr("x", main_group_width * 0.5)
        .attr("y", 25)
        .style("text-anchor", "middle");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>


您还可以遍历 item_groups 选择定义的组(main_groups.selectAll("g") 中每个父元素一个组)并分配您的属性。例如

item_groups_enter.forEach(function(g, i) {
    var parentdatum = d3.select(g.parentNode).datum();
    var h = available_height/parentdatum.values.length;
    var selection = d3.selectAll(g);

   selection.attr("transform", function (d, i) {
       return ("translate(0, " + h * i + ")");
   });

   selection.select('rect')
        .attr("height", h);

   selection.select('text')
        .attr("y", h/2);
});

您可以使用每个组上定义的 parentNode 来确定正确的父数据。

var data = [{
    group: "Fruits",
    values: ["Apple", "Banana", "Pear", "Plum"]
}, {
    group: "Cakes",
    values: ["Chocolate Cake", "Red Velvet Cake", "Carrot Cake"]
}, {
    group: "Dogs",
    values: ["Spaniel", "Chow", "Dachshund", "Bulldog", "Beagle", "Boxer", "Pug"]
}]

// Get a handle on the svg HTML element
var svg = d3.select("body").append("svg")
    .attr("width", 500)
    .attr("height", 500)

// Calculate spacing
var available_width = parseInt(svg.style("width"));
var available_height = parseInt(svg.style("height"));
var main_group_width = available_width / data.length;

// Define the colours to use
var colours = d3.scale.category10();

// Make an HTML group for each of the groups in the data
var main_groups = svg.selectAll("g")
    .data(data);

// For each datum entered, append a new HTML group
main_groups.enter()
    .append("g")
    .classed("main-group", true)
    .attr("transform", function (d, i) {
    return ("translate(" + i * main_group_width + ", 0)");
})

// Append a new group, an "item group" for each of the values in each of the main groups
var item_groups = main_groups.selectAll("g")
    .data(function (d) {
        return (d.values)
    });

var item_groups_enter = item_groups.enter()
    .append("g")
    .classed("item-group", true);


item_groups_enter.append("rect")
    .attr("width", main_group_width)
    .attr("fill", function (d, i) {
        return colours(i)
    });

item_groups_enter.append("text")
    .text(function (d) {
     return (d)
 })
    .attr("x", main_group_width * 0.5)
    .attr("y", 25)
 .style("text-anchor", "middle");


item_groups_enter.forEach(function(g, i) {
    var parentdatum = d3.select(g.parentNode).datum();
 var h = available_height/parentdatum.values.length;
    var selection = d3.selectAll(g);
    
   selection.attr("transform", function (d, i) {
       return ("translate(0, " + h * i + ")");
   });
    
   selection.select('rect')
  .attr("height", h);
    
   selection.select('text')
  .attr("y", h/2);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>