Reputation: 15732
I want to add new bars to existing d3 bar chart and make it real time graph.
I can see the bars are getting updated but labels are not aligning themselves when the bars rescales.
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10, "%");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "y axis")
.append("text") // just for the title (ticks are automatic)
.attr("transform", "rotate(-90)") // rotate the text!
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
function draw(data) {
x.domain(data.map(function(d) {
return d.letter;
}));
y.domain([0, d3.max(data, function(d) {
return d.frequency;
})]);
var labels = svg
.selectAll(".bartext")
.data(data, function(d) {
return d.letter;
});
labels
.exit()
.remove();
labels
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("x", function(d, i) {
return x(d.letter) + 7.5;
})
.attr("y", function(d, i) {
return height + 15;
})
.text(function(d, i) {
return d.letter;
});
svg.select(".y.axis").transition().duration(300).call(yAxis)
var bars = svg.selectAll(".bar").data(data, function(d) {
return d.letter;
})
bars.exit()
.transition()
.duration(300)
.remove();
bars.enter().append("rect")
.attr("class", "bar");
bars.transition().duration(300).attr("x", function(d) {
return x(d.letter);
})
.attr("width", 15)
.attr("y", function(d) {
return y(d.frequency);
})
.attr("height", function(d) {
return height - y(d.frequency);
});
}
var data1 = [{
"letter": 'A',
"frequency": .00167
}];
var data2 = [{
"letter": 'A',
"frequency": .01167
},{
"letter": 'I',
"frequency": .01477
}];
draw(data1);
setTimeout(function() {
draw(data2);
}, 2000);
https://jsfiddle.net/foh7cgst/
Upvotes: 0
Views: 609
Reputation: 32327
Instead of this:
labels
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("x", function(d, i) {
return x(d.letter) + 7.5;
})
.attr("y", function(d, i) {
return height + 15;
})
.text(function(d, i) {
return d.letter;
});
Do this:
labels
.enter()
.append("text")
.attr("class", "bartext");
//update all the bar text.
svg
.selectAll(".bartext").attr("text-anchor", "middle")
.transition().duration(300)
.attr("fill", "black")
.attr("x", function(d, i) {
return x(d.letter) + 7.5;
})
.attr("y", function(d, i) {
return height + 15;
})
.text(function(d, i) {
return d.letter;
});
In the first case it did not work, because the attributes will get updated only for new data, updated data will not get updated to the DOM.
working code here
Upvotes: 1
Reputation: 19193
Here's the relevant part of the selection.enter()
documentation:
var update_sel = svg.selectAll("circle").data(data)
update_sel.attr(/* operate on old elements only */)
update_sel.enter().append("circle").attr(/* operate on new elements only */)
update_sel.attr(/* operate on old and new elements */)
update_sel.exit().remove() /* complete the enter-update-exit pattern */
As you can see, when you append to an enter selection, the operations that follow only target the new elements that were appended.
If you want to target both new and old elements, you should operate on the update selection after entering the nodes.
So, using your example code that is inside the draw
function, this:
labels.enter().append("text")
.attr("class", "bartext")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("x", function(d, i) {
return x(d.letter) + 7.5;
})
.attr("y", function(d, i) {
return height + 15;
})
.text(function(d, i) {
return d.letter;
});
Should be changed to this:
labels.enter().append("text")
.attr("class", "bartext")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("y", height + 15);
labels
.attr("x", function(d) {
return x(d.letter) + 7.5;
})
.text(function(d) {
return d.letter;
});
Upvotes: 2