I have a D3 visualization with a map and a bar chart. I am trying to get the bar chart to change depending on which circle on the map is clicked. Not sure how to do this. I have a function in my bar_chart.js file named update(newData) and a few extra arrays for the different circles on the map. Here is the link to the bl.ocks for the map and bar char.
js code for map
var myData = [21, 3, 5, 21, 15];
//Width and height
var w = 200;
var h = 125;
var yScale = null;
function draw(initialData) {
var xScale = d3.scale.ordinal()
.domain(d3.range(initialData.length))
.rangeRoundBands([0, w], 0.05);
yScale = d3.scale.linear()
.domain([0, d3.max(initialData)])
.range([0, h]);
//Create SVG element
var svg = d3.select("#chart")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(initialData)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", "steelblue");
svg.selectAll("text")
.data(initialData)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
}
draw(myData);
//update function
function update(newData) {
yScale.domain([0, d3.max(newData)]);
var rects = d3.select("#chart svg")
.selectAll("rect")
.data(newData);
// enter selection
rects
.enter().append("rect");
// update selection
rects
.transition()
.duration(300)
.attr("y", function(d) {
return h - yScale(d);
})
.attr("height", function(d) {
return yScale(d);
})
// exit selection
rects
.exit().remove();
var texts = d3.select("#chart svg")
.selectAll("text")
.data(newData);
// enter selection
texts
.enter().append("rect");
// update selection
texts
.transition()
.duration(300)
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.text(function(d) {
return d;
})
// exit selection
texts
.exit().remove();
}
var mk = [10,17,20,14,8];
var cn = [18,4,9,20,15];
var nd = [5,12,7,15,21];
d3.select("#update").on("click", function() { update(newData); });
You have to incorporate the barchart data in your cities.csv file.
In the on-click handler of cities.csv where you show the tooltip you have to transform the data from the CSV into an array and call the bar chart update() method with this array.
One way of doing is to replace the , from the bar chart data with another char and split the string and convert the parts to numbers.
var cityData = d.barchart.split('#').map(Number);
update(cityData);
You also have to set the attributes of the new rects and texts of the bar chart. And the x-position will change if the number of bars change.
I have a visualization task that I need to make it done with d3.js. Here's my code.
var w = 700;
var h = 500;
var offset = 100;
var padding = 20;
var colors = d3.scale.category10();
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var texts = function(ds,ds2){
var stack = d3.layout.stack();
stack_data = stack(ds);
var xScale = d3.scale.ordinal()
.domain(d3.range(ds[0].length))
.rangeRoundBands([0, w-offset], 0.50);
var yScale = d3.scale.linear()
.domain([0,
d3.max(stack_data, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y -20;
});
})
])
.range([padding, h-50]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(ds[0].length);
gs = svg.selectAll("g").data(stack_data);
for (var i5 = 0; i5 < ds.length; i5++) {
gs.enter()
.append("g")
.attr("class", "stacked_bars")
.attr("fill", function(d, i) {
return colors(i);
});
asd = gs.selectAll("rect").data(function(d) { return d; });
asd.enter().append("rect");
asd.transition()
.duration(1000)
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d.y0) - yScale(d.y);
})
.attr("height", function(d) {
return yScale(d.y);
})
.attr("width", xScale.rangeBand())
.attr("class", "rectbar");
};
gs.append("g") // add a group element to the svg
.attr("class", "axis") //Assign class "axis" to group
.attr("transform", "translate(0," + (h - padding) + ")") // shift the axis to bottom
.call(xAxis); // call the axis function
gs.exit().remove();
}
res = dataGenerator("Europe");
dataset = res[0];
dataset2 = res[1];
texts(dataset,dataset2);
d3.select("#selector").on("change", function() {
cont = d3.select(this).property('value');
res = dataGenerator(cont)
dataset = res[0]
dataset2 = res[1]
//svg.selectAll(".sym").remove()
texts(dataset,dataset2);
});
It basically gets the data and generates stacked bars. When user uses the select element on the page, it updates the data and generates new results. It successfully gets the first results and when user selects another option, it makes it happen also. But when user tries to use select part once again. It only generates bars for dataset's first item.
So, in this particular case I have countries and their data as numbers, at first load and first update it successfully shows all but when it comes to second update, it only generate bars for first country in dataset. It's been hours that I'm trying to fix this. I know I only have a little mistake but couldn't make it to solve.
Also here's the jsfiddle of the code: https://jsfiddle.net/510ep9ux/4/
Since I'm new at d3.js, I may not understand the update concept well.
So, any guesses?
Solved, using two separate functions textsInit and textsUpdate :
https://jsfiddle.net/qxqdp36x/2/
Essentially you need to separate initialization and update logic, and avoid re-creating elements when updating, that causes unintended behaviours.
Also, the variables gs and asd needs to be global to be accessible to both functions.
var textsInit = function(ds, ds2) {
var stack = d3.layout.stack();
stack_data = stack(ds);
var xScale = d3.scale.ordinal()
.domain(d3.range(ds[0].length))
.rangeRoundBands([0, w - offset], 0.50);
var yScale = d3.scale.linear()
.domain([0,
d3.max(stack_data, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y - 20;
});
})
])
.range([padding, h - 50]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(ds[0].length);
gs = svg.selectAll("g").data(stack_data);
bars = gs.enter();
bars.append("g")
.attr("class", "stacked_bars")
.attr("fill", function(d, i) {
return colors(i);
});
asd = gs.selectAll("rect").data(function(d) {
return d;
});
asd.enter().append("rect");
asd.transition()
.duration(1000)
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d.y0) - yScale(d.y);
})
.attr("height", function(d) {
return yScale(d.y);
})
.attr("width", xScale.rangeBand())
.attr("class", "rectbar");
gs.append("g") // add a group element to the svg
.attr("class", "axis") //Assign class "axis" to group
.attr("transform", "translate(0," + (h - padding) + ")") // shift the axis to bottom
.call(xAxis); // call the axis function
}
And:
var textsUpdate = function(ds, ds2) {
var stack = d3.layout.stack();
stack_data = stack(ds);
var xScale = d3.scale.ordinal()
.domain(d3.range(ds[0].length))
.rangeRoundBands([0, w - offset], 0.50);
var yScale = d3.scale.linear()
.domain([0,
d3.max(stack_data, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y - 20;
});
})
])
.range([padding, h - 50]);
gs.data(stack_data);
asd = gs.selectAll("rect").data(function(d) { return d; });
asd.transition()
.duration(1000)
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d.y0) - yScale(d.y);
})
.attr("height", function(d) {
return yScale(d.y);
})
.attr("width", xScale.rangeBand())
.attr("class", "rectbar");
}
Edited to fix a small bug, updating the asd selection's data.
I made 2 simple but crucial changes to your code.
https://jsfiddle.net/guanzo/510ep9ux/6/
From
gs = svg.selectAll("g").data(stack_data);
to
gs = svg.selectAll("g.stacked_bars").data(stack_data);
The axis is also contained in a g element, so you have to ensure you're only selecting elements that are used for your data, and not unrelated elements.
From
gs.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
to
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
If you go into the browser inspector you'll see that you have an axis element for EVERY stacked_bars element, you only need 1 obviously. It only looks like there's 1 axis because they're absolutely positioned and stacked on top of each other.
I changed it so that the axis is appended when the svg is created, and every time new data is selected, the axis will update itself.
I'm building a grouped bar chart by nesting a .csv file. The chart will also be viewable as a line chart, so I want a nesting structure that suits the line object. My original .csv looks like this:
Month,Actual,Forecast,Budget
Jul-14,200000,-,74073.86651
Aug-14,198426.57,-,155530.2499
Sep-14,290681.62,-,220881.4631
Oct-14,362974.9,-,314506.6437
Nov-14,397662.09,-,382407.67
Dec-14,512434.27,-,442192.1932
Jan-15,511470.25,511470.25,495847.6137
Feb-15,-,536472.5467,520849.9105
Mar-15,-,612579.9047,596957.2684
Apr-15,-,680936.5086,465313.8723
May-15,-,755526.7173,739904.081
Jun-15,-,811512.772,895890.1357
and my nesting is like this:
d3.csv("data/net.csv", function(error, data) {
if (error) throw error;
var headers = d3.keys(data[0]).filter(function(head) {
return head != "Month";
});
data.forEach(function(d) {
d.month = parseDate(d.Month);
});
var categories = headers.map(function(name) {
return {
name: name, // "name": the csv headers except month
values: data.map(function(d) {
return {
date: d.month,
rate: +(d[name]),
};
}),
};
});
The code to build my chart is:
var bars = svg.selectAll(".barGroup")
.data(data) // Select nested data and append to new svg group elements
.enter()
.append("g")
.attr("class", "barGroup")
.attr("transform", function (d) { return "translate(" + xScale(d.month) + ",0)"; });
bars.selectAll("rect")
.data(categories)
.enter()
.append("rect")
.attr("width", barWidth)
.attr("x", function (d, i) { if (i < 2) {return 0;} else {return xScale.rangeBand() / 2;}})
.attr("y", function (d) { return yScale(d.rate); })
.attr("height", function (d) { return h - yScale(d.rate); })
.attr("class", function (d) { return lineClass(d.name); });
The g elements are fine and the individual bars are being mapped to them, with the x value and class applied correctly.
My problem comes in accessing the data for 'rate' for the height and y value of the bars. In the form above it gives a NaN. I've also tried using the category data to append g elements and then appending the rects with:
.data(function(d) { return d.values })
This allows me to access the rate data, but maps all 36 bars to each of the rangeBands.
It also works fine in a flatter data structure, but I can't seem to use it when it's nested two levels down, despite looking through a great many examples and SO questions.
How do I access the rate data?
In response to Cyril's request, here's the full code:
var margin = {top: 20, right: 18, bottom: 80, left: 50},
w = parseInt(d3.select("#bill").style("width")) - margin.left - margin.right,
h = parseInt(d3.select("#bill").style("height")) - margin.top - margin.bottom;
var customTimeFormat = d3.time.format.multi([
[".%L", function(d) { return d.getMilliseconds(); }],
[":%S", function(d) { return d.getSeconds(); }],
["%I:%M", function(d) { return d.getMinutes(); }],
["%I %p", function(d) { return d.getHours(); }],
["%a %d", function(d) { return d.getDay() && d.getDate() != 1; }],
["%b %d", function(d) { return d.getDate() != 1; }],
["%b", function(d) { return d.getMonth(); }],
["%Y", function() { return true; }]
]);
var parseDate = d3.time.format("%b-%y").parse;
var displayDate = d3.time.format("%b %Y");
var xScale = d3.scale.ordinal()
.rangeRoundBands([0, w], .1);
var xScale1 = d3.scale.linear()
.domain([0, 2]);
var yScale = d3.scale.linear()
.range([h, 0])
.nice();
var xAxis = d3.svg.axis()
.scale(xScale)
.tickFormat(customTimeFormat)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.innerTickSize(-w)
.outerTickSize(0);
var svg = d3.select("#svgCont")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var thous = d3.format(",.0f")
var lineClass = d3.scale.ordinal().range(["actual", "forecast", "budget"]);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<p id='date'>" + displayDate(d.date) + "</p><p id='value'>$" + thous(d.rate);
})
d3.csv("data/net.csv", function(error, data) {
if (error) throw error;
var headers = d3.keys(data[0]).filter(function(head) {
return head != "Month";
});
data.forEach(function(d) {
d.month = parseDate(d.Month);
});
var categories = headers.map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
date: d.month,
rate: +(d[name]),
};
}),
};
});
var min = d3.min(categories, function(d) {
return d3.min(d.values, function(d) {
return d.rate;
});
});
var max = d3.max(categories, function(d) {
return d3.max(d.values, function(d) {
return d.rate;
});
});
var minY = min < 0 ? min * 1.2 : min * 0.8;
xScale.domain(data.map(function(d) { return d.month; }));
yScale.domain([minY, (max * 1.1)]);
var barWidth = headers.length > 2 ? xScale.rangeBand() / 2 : xScale.rangeBand() ;
svg.call(tip);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var bars = svg.selectAll(".barGroup")
.data(data)
.enter()
.append("g")
.attr("class", "barGroup")
.attr("transform", function (d) { return "translate(" + xScale(d.month) + ",0)"; });
bars.selectAll("rect")
.data(categories)
.enter()
.append("rect")
.attr("width", barWidth)
.attr("x", function (d, i) { if (i < 2) {return 0;} else {return xScale.rangeBand() / 2;}})
.attr("y", function (d) { return yScale(d.rate); })
.attr("height", function (d) { return h - yScale(d.rate); })
.attr("class", function (d) { return lineClass(d.name) + " bar"; });
var legend = svg.selectAll(".legend")
.data(headers)
.enter()
.append("g")
.attr("class", "legend");
legend.append("line")
.attr("class", function(d) { return lineClass(d); })
.attr("x1", 0)
.attr("x2", 40)
.attr("y1", function(d, i) { return (h + 30) + (i *14); })
.attr("y2", function(d, i) { return (h + 30) + (i *14); });
legend.append("text")
.attr("x", 50)
.attr("y", function(d, i) { return (h + 32) + (i *14); })
.text(function(d) { return d; });
svg.selectAll(".bar")
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
});
Update 18 Feb '16.
It seems I haven't explained what I was trying to do sufficiently well. The line and bar versions of the chart will be seen separately, i.e. users can see either one according to input to a select element. Also note that I don't have control over how the data comes in initially.
I have a version of exactly how it should work here.
This question was raised when I was still working through it, but I never solved the issue – I used a workaround of doing two separate nests of the data.
Link to jsfiddle:
https://jsfiddle.net/sladav/rLh4qwyf/1/
I think the root of the issue is that you want to use two variables that do not explicitly exist in your original data set: (1) Category and (2) Rate.
Your data is formatted in a wide format in that each category gets its own variable and the value for rate exists at the crossroads of month and one of the given categories. I think the way you're nesting ultimately is or at least should address this, but it is unclear to me if or where something gets lost in translation. Conceptually, I think it makes more sense to start with an organization that matches what you are trying to accomplish. I reformatted the original data and approached it again - on a conceptual level the nesting seems straightforward and simple...
NEW COLUMNS:
Month: Time Variable; mapped to X axis
Category: Categorical values [Actual, Forecast, Budget]; used to group/color
Rate: Numerical value; mapped to Y axis
Reorganized CSV (dropped NULLs):
Month,Category,Rate
Jul-14,Actual,200000
Aug-14,Actual,198426.57
Sep-14,Actual,290681.62
Oct-14,Actual,362974.9
Nov-14,Actual,397662.09
Dec-14,Actual,512434.27
Jan-15,Actual,511470.25
Jan-15,Forecast,511470.25
Feb-15,Forecast,536472.5467
Mar-15,Forecast,612579.9047
Apr-15,Forecast,680936.5086
May-15,Forecast,755526.7173
Jun-15,Forecast,811512.772
Jul-14,Budget,74073.86651
Aug-14,Budget,155530.2499
Sep-14,Budget,220881.4631
Oct-14,Budget,314506.6437
Nov-14,Budget,382407.67
Dec-14,Budget,442192.1932
Jan-15,Budget,495847.6137
Feb-15,Budget,520849.9105
Mar-15,Budget,596957.2684
Apr-15,Budget,465313.8723
May-15,Budget,739904.081
Jun-15,Budget,895890.1357
With your newly formatted data, you start by using d3.nest to GROUP your data explicitly with the CATEGORY variable. Now your data exists in two tiers. The first tier has three groups (one for each category). The second tier contains the RATE data for each line/set of bars. You have to nest your data selections as well - the first layer is used to draw the lines, the second layer for the bars.
Nesting your data:
var nestedData = d3.nest()
.key(function(d) { return d.Category;})
.entries(data)
Create svg groups for your grouped, 1st-tier data:
d3.select(".plot-space").selectAll(".g-category")
.data(nestedData)
.enter().append("g")
.attr("class", "g-category")
Use this data to add your lines/paths:
d3.selectAll(".g-category").append("path")
.attr("class", "line")
.attr("d", function(d){ return lineFunction(d.values);})
.style("stroke", function(d) {return color(d.key);})
Finally, "step into" 2nd-tier to add bars/rect:
d3.selectAll(".g-category").selectAll(".bars")
.data(function(d) {return d.values;})
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {return x(d.Month);})
.attr("y", function(d) {return y(d.Rate);})
.attr("width", 20)
.attr("height", function(d) {return height - y(d.Rate)})
.attr("fill", function(d) {return color(d.Category)})
This is a straightforward approach (to me at least), in that you take it one category at a time, using the grouped data to draw a line, then individual data points to draw the bars.
LAZY EDIT:
To get category bars side by side
Create ordinal scale mapping category to [1,nCategories]. Use this to dynamically offset bars with something like
translate( newScale(category)*barWidth )
To show either bars or lines (not both)
Create a function that selects bars/lines and transitions/toggles their visibility/opacity. Run when your drop-down input changes and with the drop-down input as input to the function.
The problem, I belive, is that you are binding the categories array to the bars selection, like this:
bars.selectAll("rect").data(categories)
As far as I can see (whithout a running demo) categories is an array with only four values (one for each category).
You have to go one step 'deeper' in your nested data structure.
To draw a set of bars for each category you would need to iterate over categories and bind the values array that contains the actual values to the selection.
Something like:
categories.each(function (category) {
var klass = category.name;
bars.selectAll("rect ." + klass)
.data(category.values)
.enter()
.append("rect")
.attr("class", klass)
.attr("width", barWidth)
.attr("x", function (d, i) { /* omitted */})
.attr("y", function (d) { return yScale(d.rate); })
.attr("height", function (d) { return h - yScale(d.rate); });
});
---- Edit
Instead of the above code, think about drawing the bars just like you do with the lines. Like this:
var bars = svg.selectAll(".barGroup")
.data(categories)
.enter()
.append("g")
.attr("class", function (d) { return lineClass(d.name) + "Bar barGroup"; })
.attr("transform", function (d, i) {
var x = i > 1 ? xScale.rangeBand() / 2 : 0;
return "translate(" + x + ",0)";
})
.selectAll('rect')
.data(function (d) { return d.values; })
.enter()
.append("rect")
.attr("class", "bar")
.attr("width", barWidth)
.attr("x", function (d, i) { return xScale(d.date); })
.attr("y", function (d, i) { return yScale(d.rate); })
.attr("height", function (d) { return h - yScale(d.rate); });
Below is my d3 code, it works perfectly if I mention the dataset with its numbers. However, when i want to take data from a csv file, it doesn't not accept it.
*ERROR
Error: Invalid value for <circle> attribute cx="NaN"
Here how the csv looks like:
t Or
16610 20635
14920 19532
13131 14814
15882 15745
15769 14993
15989 22557
14895 15387
17915 19758
Although if I try in google chrome,
console.log(dataset)
I get the data from csv but when i run it to apply, it just doesn't work in the browser.
I am using brackets as my IDE and google chrome as my default browser.
<body>
<h1> Hello World!! </h1>
<script type="text/javascript">
var dataset;
d3.csv("t.csv", function(d) {
dataset = d;
var h = 500;
var w = 1200;
var padding = 30;
var xscale = d3.scale.linear()
.domain([0,d3.max(dataset, function(d) { return d[0];})])
.range([padding, w- padding*2]);
var yscale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) { return d[1];})])
.range([h-padding,padding]);
var rscale = d3.scale.linear()
.domain([0,d3.max(dataset , function(d) { return d[1];})])
.range([5,30]);
var xAxis = d3.svg.axis()
.scale(xscale)
.orient("bottom");
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var circle = svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { return xscale(d[0]);})
.attr("cy", function(d) { return yscale(d[1]);})
.attr("r",function(d) { return rscale(d[1]);})
.on("mouseover", function(){d3.select(this).style("fill", "yellow");})
.on("mouseout", function(){d3.select(this).style("fill", function(dataset) { return "rgb(0,0," +(d*10) + ")";});});
var text = svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) { return d[0] + "," + d[1];})
.attr("x", function(d) { return xscale(d[0]);})
.attr("y", function(d) { return yscale(d[1]);})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill" ,"red");
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0, " + (h - padding) +")")
.call(xAxis);
});
</script>
</body>
I just tweaked my above code. using the below which i found right here
It worked
var dataset;
d3.csv("t.csv", function(error, d) {
dataset = d.map(function(d) { return [ +d["t"], +d["Or"] ]; });
I am trying to follow Mike Bostock's tutorial on d3js (http://mbostock.github.io/d3/tutorial/bar-2.html) to understand how to update charts dynamically but I am facing some hurdles.
In my chart, my bars on the left, rather than being simply removed, are sent behind my chart and I can't figure out why:
JS:
var t = 1297110663, // start time (seconds since epoch)
v = 70, // start value (subscribers)
data = d3.range(33).map(next); // starting dataset
function next() {
return {
time: ++t,
value: v = ~~Math.max(10, Math.min(90, v + 10 * (Math.random() - .5)))
};
}
setInterval(function(){
data.shift();
data.push(next());
console.log(data);
redraw();
}, 1000);
var w = 20,
h =80;
var x = d3.scale.linear()
.domain([0, 1])
.range([0, w]);
var y = d3.scale.linear()
.domain([0, 100])
.rangeRound([0, h]);
var chart = d3.select(".container").append("svg")
.attr("class", "chart")
.attr("width", w * data.length - 1)
.attr("height", h);
chart.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d, i){ return x(i) - 0.5; })
.attr("y", function(d) { return h - y(d.value) - .5; })
.attr("width", w)
.attr("height", function(d) { return y(d.value); });
function redraw(){
console.log(data);
var rect = chart.selectAll('rect')
.data(data, function(d){ return d.time; });
rect.enter().insert("rect")
.attr("x", function(d, i) { return x(i + 1) - .5; })
.attr("y", function(d) { return h - y(d.value) - .5; })
.attr("width", w)
.attr("height", function(d) { return y(d.value); })
rect.transition() // Shouldn't I use .update() here?
.duration(1000)
.attr("x", function(d, i) { return x(i) - .5; });
rect.exit().transition()
.duration(1000)
.attr('x', function(d, i) { return x(i - 1) - .5})
.remove();
}
Here is the jsfiddle: http://jsfiddle.net/kkMR4/
Another thing I don't understand is why we dont use .update()? If I understand correctly .enter() is used to create the DOM element where data didnt find any match in the DOM and .exit() is used to find the DOM elements which are not in data, so shouldn't I use update() to move all the other column to the left?
Many thanks
Best
The problem is in this block:
rect.exit().transition()
.duration(1000)
.attr('x', function(d, i) { return x(i - 1) - .5})
.remove();
The third line (.attr), reassigns the coordinates. If you want them to truly exit, you can remove this line.
rect.exit().transition()
.duration(1000)
.remove();