D3 - 让孩子填充父母组

时间:2015-09-22 19:47:51

标签: javascript css layout d3.js spacing

我正在尝试为D3中的单嵌套数据创建一个简单的矩形结构。我希望结果看起来像下图:

The desired menu structure

换句话说,每个组的项目都应调整大小,以便所有组占用相同的空间。

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 s'到item_group的数据(具体来说,孩子的数量),但我不确定如何做到这一点。我确实尝试在childCount上设置自定义属性main_group,但代码变得非常混乱,引用父节点(稍后是祖父节点)。

这样做的方法是什么?我不确定我是否应该考虑D3或CSS方面的解决方案?

1 个答案:

答案 0 :(得分:1)

使用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>
&#13;
&#13;
&#13;

您还可以遍历由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来确定正确的父数据。

&#13;
&#13;
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);
});
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
&#13;
&#13;
&#13;