Reputation: 873
I tried to toggle the data using d3.interval and render the data as bar graphs on my svg.
As below.
interval--
d3.interval(function(){update(data);
flag =! flag;
},1000)
rendering--
function update(input){
var value = flag ? 'revenue':'profit'
x.domain(input.map(function(d){return d.month}));
y.domain([0,d3.max(input, function(d){return d[value]})])
var xAxisCall = d3.axisBottom(x);
var yAxisCall = d3.axisLeft(y).tickFormat(function(d){return '$'+d})
xAxisGroup.call(xAxisCall.tickSizeOuter(0))
yAxisGroup.call(yAxisCall.tickSizeOuter(0))
var rects = svg.selectAll('rect').data(data)
rects.exit().remove()
rects.enter().append('rect').attr('y', (d)=>{return height-y.range([0,height])(d[value])})
.attr('x', (d)=>{ return 50+x(d.month)})
.attr('height',(d)=>{return y(d[value])})
.attr('width',x.bandwidth())
.attr('fill','grey')
}
However, it doesn't toggle the graph depending on the data fed into the rectangles. It only worked when I put .append() at the end of rectangle rendering like below.
rects.attr('y', (d)=>{return height-y.range([0,height])(d[value])})
.attr('x', (d)=>{ return 50+x(d.month)})
.attr('height',(d)=>{return y(d[value])})
.attr('width',x.bandwidth())
.attr('fill','grey')
.enter().append('rect')
Why is it that I need to add enter and append at the end? The reason that I don't get this is Even if I put the .enter().append('rect') at the beginning, the properties of the rect can be redefined and updated.
However, the properties of the rectangles weren't updated once I put the enter() argument beforehand.
I tested out by adding 'console.log' like below.
rects.enter().append('rect').attr('fill',function(){return flag? "rgba(100,0,0,0.2)":'rgba(0,100,0,0.2)'}).attr('y', (d)=>{return height-y.range([0,height])(d[value])})
.attr('x', (d)=>{ return 50+x(d.month)})
.attr('height',(d)=>{console.log(d[value])
;return y(d[value])})
.attr('width',x.bandwidth())
It doesn't log anything, it logs only when I put enter at the end.
Why all the arguments don't run?
Upvotes: 2
Views: 39
Reputation: 7162
D3 uses the data join, which doens't act on elements directly, but describes their transformations. When you do:
var rects = svg.selectAll('rect').data(data)
you're not selecting all the rectangles, but only those in the "update" selection. Here's a more detailled explaination by the author of D3.
Nowadays, it's recommended to use the selection.join()
method which makes selection-based code clearer:
svg.selectAll('rect')
.data(data)
.join(
enter => enter.append('rect')
.attr('fill', 'grey'),
update => update
.attr('x', (d) => 50 + x(d.month))
.attr('y', (d) => height-y.range([0,height])(d[value]))
.attr('width', x.bandwidth())
.attr('height', (d) => y(d[value])),
exit => exit.remove()
)
Upvotes: 1
Reputation: 102174
In your first case you have an enter selection but nothing changing your update selection. In your second case you're changing your update selection while doing nothing to your enter selection.
Write your update and enter selections clearly and separately. This normally leads to duplicated code... if you don't like those duplications, use merge()
:
var rects = svg.selectAll('rect').data(data)
rects.exit().remove()
rects = rects.enter()
.append('rect')
.attr('fill', 'grey')
.merge(rects)
.attr('y', (d) => {
return height - y.range([0, height])(d[value])
})
.attr('x', (d) => {
return 50 + x(d.month)
})
.attr('height', (d) => {
return y(d[value])
})
.attr('width', x.bandwidth())
Upvotes: 3