Updating D3 column chart with different values and different data sizes
- by mbeasley
Background
I am attempting to create a reusable chart object with D3.js. I have setup a chart() function that will produce a column chart. On a click event on any of the columns, the chart will update with a new random data array that will contain a random number of data points (i.e. the original chart could have 8 columns, but upon update, could have 20 columns or 4 columns).
Problem
Say I have 8 data points (and thus 8 columns) in my original dataset. When I update the chart with random data, the columns appropriately adjust their height to the new values - but new bars aren't added. Additionally, while the width of the columns appropriately adjust to accommodate the width of the container and the new number of data points, if that number of data points is less than the original set, then some of those columns from the original dataset will linger until the number of data points is greater than or equal than the original.
My end goal is to have new data dynamically added or old data outside of the range of the new data count dynamically removed.
I've created a jsfiddle of the behavior. You may have to click the columns a couple of times to see the behavior I'm describing. Additionally, I've pasted my code below.
Thanks in advance!
function chart(config) {
// set default options
var defaultOptions = {
selector: '#chartZone',
class: 'chart',
id: null,
data: [1,2,6,4, 2, 6, 7, 2],
type: 'column',
width: 200,
height: 200,
callback: null,
interpolate: 'monotone'
};
// fill in unspecified settings in the config with the defaults
var settings = $.extend(defaultOptions, config);
function my() { // generate chart with this function
var w = settings.width,
h = settings.height,
barPadding = 3,
scale = 10,
max = d3.max(settings.data);
var svg = d3.select(settings.selector) // create the main svg container
.append("svg")
.attr("width",w)
.attr("height",h);
var y = d3.scale.linear().range([h, 0]),
yAxis = d3.svg.axis().scale(y).ticks(5).orient("left"),
x = d3.scale.linear().range([w, 0]);
y.domain([0, max]).nice();
x.domain([0, settings.data.length - 1]).nice();
var rect = svg.selectAll("rect")
.data(settings.data)
.enter()
.append("rect")
.attr("x", function(d,i) {
return i * (w / settings.data.length);
})
.attr("y", function(d) {
return h - h * (d / max);
})
.attr("width", w / settings.data.length - barPadding)
.attr("height", function(d) {
return h * (d / max);
})
.attr("fill", "rgb(90,90,90)");
svg.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(-4,0)")
.call(yAxis);
svg.on("click", function() {
var newData = [], maxCap = Math.round(Math.random() * 100);
for (var i = 0; i < Math.round(Math.random()*100); i++) {
var newNumber = Math.random() * maxCap;
newData.push(Math.round(newNumber));
}
newMax = d3.max(newData);
y.domain([0, newMax]).nice();
var t = svg.transition().duration(750);
t.select(".y.axis").call(yAxis);
rect.data(newData)
.transition().duration(750)
.attr("height", function(d) {
return h * (d / newMax);
})
.attr("x", function(d,i) {
return i * (w / newData.length);
})
.attr("width", w / newData.length - barPadding)
.attr("y", function(d) {
return h - h * (d / newMax);
});
});
}
my();
return my;
}
var myChart = chart();