Reputation: 107
I´m wondering if it is possible to append multiple rectangles per datapoint. In my example there are three datapoints. For each I try to create 2 rectangles. In the end there have to be 6 rectangles. 3 in red, 3 in blue. IMAGE
According to this answer, I tried the following solution:
var svg = d3.select("body").append("svg");
svg.selectAll("rect")
.data([10,60,120])
.enter()
.append("g")
.append("rect")
.attr("width", 20)
.attr("height", 20)
.attr("x", 20)
.attr("y", function(d) {return d})
.attr("fill", "red")
.selectAll("rect")
.data(function(d) { return d3.range(d); })
.enter()
.append("rect")
.attr("width", 20)
.attr("height", 20)
.attr("x", 60)
.attr("y", function(d) {return d})
.attr("fill", "blue");
Unfortunately the blue rectangles are created inside the red ones. Any idea how to achieve this? Here is an EXAMPLE
Upvotes: 5
Views: 8568
Reputation: 9520
You're successfully creating extra rectangles, but unfortunately they are all nested inside the first three rectangles. If you have a web developer extension on your browser, select one of the rectangles and then view the source to see.
If you want to append the rectangles, rather than nest them inside the original rectangle, you need to append both rectangles to your g
node. When you bind the data and add your g
nodes, assign those nodes to a variable:
var svg = d3.select("body").append("svg");
var nodes = svg.selectAll(".rect")
.data([10,60,120])
.enter()
.append("g")
.classed('rect', true)
Then you can append the two rectangles to the g
nodes:
// red rectangles
nodes.append("rect")
.attr("width", 20)
.attr("height", 20)
.attr("x", 20)
.attr("y", function(d) {return d})
.attr("fill", "red")
// blue ones
nodes.append("rect")
.attr("width", 20)
.attr("height", 20)
.attr("x", 120)
.attr("y", function(d) {return d})
.attr("fill", "blue")
Note that if you do this:
var nodes = svg.selectAll("rect")
.data([10,60,120])
.enter()
.append("g")
.append("rect")
.attr("width", 20)
.attr("height", 20)
.attr("x", 20)
.attr("y", function(d) {return d})
.attr("fill", "red")
nodes
will be a reference to the rect
elements, as they were the last thing added, and anything appended to nodes
will be added inside the rect
elements.
Note also that you only need to bind the data once, to the g
elements; it is automatically inherited by all the child nodes of those g
elements.
Here's the completed example:
var svg = d3.select("body").append("svg");
var g = svg.selectAll(".rect")
.data([10,60,120])
.enter()
.append("g")
.classed('rect', true)
g.append("rect")
.attr("width", 20)
.attr("height", 20)
.attr("x", 20)
.attr("y", function(d) {return d})
.attr("fill", "red")
g.append("rect")
.attr("width", 20)
.attr("height", 20)
.attr("x", 120)
.attr("y", function(d) {return d})
.attr("fill", "blue")
<script src="https://d3js.org/d3.v5.js"></script>
Upvotes: 16