Reputation: 460
In my previous question I asked how could I generate unique IDs for generated rectangles here
Now, I've been trying to draw small rectangles inside those rectangles. However, id="rectup0"
can only have 4 small rect with unique xlink:href
attribute
the rest of them can have 2 rectangles inside.
<body>
<div id="chart">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.js"></script>
<script type="text/javascript">
var width = 1000,
height = 250,
margin = 7,
nRect = 11,
rectWidth = (width - (nRect - 1) * margin) / nRect,
svgContainer = d3.select('#chart').append('svg')
.attr('width', width)
.attr('height', height),
svgWalkway = d3.select('#chart').append('svg')
.attr('width', '1000')
.attr('height', '150'),
svgContainer2 = d3.select('#chart').append('svg')
.attr('width', width)
.attr('height', height);
// Rectangles scaling
var firstRow = d3.range(nRect),
posxScale = d3.scaleLinear()
.domain(d3.extent(firstRow))
.range([0, width - rectWidth]);
var middleRow = d3.range(1),
posxScale3 = d3.scaleLinear()
.domain(d3.extent(middleRow))
.range([0, width - rectWidth]);
var secondRow = d3.range(nRect),
posxScale2 = d3.scaleLinear()
.domain(d3.extent(secondRow))
.range([0, width - rectWidth]);
//Drawing the actual objects HTML rects
svgContainer.selectAll('rect.up')
.data(firstRow)
.enter()
.append('rect')
.attr("id", function(d,i){ return "rectup" + i })
.attr('x', posxScale)
.attr('width', rectWidth)
.attr('height', height)
.attr('class','up');
svgWalkway.selectAll('rect.middle')
.data(middleRow)
.enter()
.append('rect')
.attr('x', posxScale3)
.attr('width','1000')
.attr('height','150')
.attr('class', 'middle');
svgContainer2.selectAll('rect.down')
.data(secondRow)
.enter()
.append('rect')
.attr('id', function (d,i) { return "rectdown" + i })
.attr('x', posxScale2)
.attr('width', rectWidth)
.attr('height', height)
.attr('class', 'down')
.attr('y', 0);
</script>
</div>
</body>
I've tried drawing similar rectangles like the previous ones (using .append()
) but so far that just put them next to svgContainer
. I've been reading some posts regarding using grouping, tried it but didn't work. Thanks in advance
UPDATE
so basically, the whole question can be resumed as adding a rect
tag right after rect id="rectup0"
so I can have
<rect id="rectup0"></rect>
<rect id="redrect" x="0" width="20" height="40"></rect>
I know JQuery
lets you add class attributes to other classes, but not sure about D3 , this will be something like adding a new rect
tag programmatically. is that even possible?
UPDATE 2
I guess it is possible with d3.insert
, with the following:
d3.select('#rectup0').insert('rect')
.attr('id', 'redrect')
.attr('x', 0)
.attr('width', '20')
.attr('height', '40');
however it gets it in the middle of the beginning and closing rect
tag like this
<rect id="rectup0">
<rect id="redrect" x="0" width="20" height="40"></rect>
</rect>
Needless to say this doesn't work because of the closing rect
tag
Upvotes: 2
Views: 1196
Reputation: 28355
You can do the trick with each
wrapper for selected data. It accepts 3 arguments: current data d
, current index i
, and whole array of data arr
. So if you need some kind of checks, should you append the small rect
, or not, you can create a helper function:
var testIndex = function(d, i, arr) {
return
// test index
i == 5 ||
// test current data
d == 10 ||
// test other array members, which would be an EnterNode here
(i > 0 && arr[i - 1]['__data__'] == 7);
}
// do checks in one line:
var testIndex = function (d, i, arr) { return i == 4 || i == 10 || i == 7; }
var data = svgContainer.selectAll('rect.up')
.data(firstRow);
data
.enter()
.each(function (d, i, arr) {
// select DOM element to add the rects
t = d3.select(this);
t
.append('rect')
// note that here we use empty the parameters list
.attr('id', 'rectup' + i)
.attr('x', posxScale)
.attr('width', rectWidth)
.attr('height', height)
.attr('class','up');
if (!testIndex(d, i, arr)) {
return;
}
t
.append('rect')
.attr('id', 'redrect' + i)
// note that we need to define x-coord similar to first rect
.attr('x', posxScale)
.attr('class', 'redrect');
});
// remove your data before redraw
data
.exit()
.remove();
Output:
<svg width="1000" height="250">
<rect id="rectup0" x="0" width="84.54545454545455" height="250" class="up"></rect>
<rect id="rectup1" x="91.54545454545456" width="84.54545454545455" height="250" class="up"></rect>
<rect id="rectup2" x="183.09090909090912" width="84.54545454545455" height="250" class="up"></rect>
<rect id="rectup3" x="274.6363636363636" width="84.54545454545455" height="250" class="up"></rect>
<rect id="rectup4" x="366.18181818181824" width="84.54545454545455" height="250" class="up"></rect>
<rect id="rectup5" x="457.72727272727275" width="84.54545454545455" height="250" class="up"></rect>
<rect id="redrect5" x="457.72727272727275" class="redrect"></rect>
<rect id="rectup6" x="549.2727272727273" width="84.54545454545455" height="250" class="up"></rect>
<rect id="rectup7" x="640.8181818181818" width="84.54545454545455" height="250" class="up"></rect>
<rect id="redrect7" x="732.3636363636365" class="redrect"></rect>
<rect id="rectup8" x="732.3636363636365" width="84.54545454545455" height="250" class="up"></rect>
<rect id="rectup9" x="823.909090909091" width="84.54545454545455" height="250" class="up"></rect>
<rect id="rectup10" x="915.4545454545455" width="84.54545454545455" height="250" class="up"></rect>
<rect id="redrect10" x="915.4545454545455" class="redrect"></rect>
</svg>
The downside of such method is that you can't provide a attr
parameter as JSON:
// wouldn't work
.attr({
id: 'redrect',
x: posxScale,
width: '20',
height: '40'
});
Upvotes: 1