I'm rather a beginner at both JS and D3.js; my JSFiddle is here.
JSHint shows no errors, so I think I'm doing the D3 wrong.
I'm trying to do the same thing as in these questions, adapting a Tributary example to run outside of that platform: "Exporting" a Tributary example that makes use of the tributary object - d3.js and Getting horizontal stack bar example to display using d3.js (I'm even adapting the same code as the latter) Unfortunately, there is no corrected JSFiddle or other example in either answer
Here's my code:
var VanillaRunOnDomReady = function() {
var margins = {
top: 12,
left: 48,
right: 24,
bottom: 24
};
var data = [
{"key":"Nonviolent", "cat1":0.69, "cat2":0.21, "cat3":0.10},
{"key":"Violent", "cat1":0.53, "cat2":0.29, "cat3":0.18}
];
var catnames = {cat1: "No mental illness",
cat2: "Mild mental illness",
cat3: "Moderate or severe mental illness"};
var svg = d3.select('body')
.append('svg')
.attr('width', width + margins.left + margins.right)
.attr('height', height + margins.top + margins.bottom)
.append('g')
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')');
var n = 3, // number of layers
m = data.length, // number of samples per layer
stack = d3.layout.stack(),
labels = data.map(function(d) {return d.key;}),
//go through each layer (cat1, cat2 etc, that's the range(n) part)
//then go through each object in data and pull out that objects's catulation data
//and put it into an array where x is the index and y is the number
layers = stack(d3.range(n).map(function(d) {
var a = [];
for (var i = 0; i < m; ++i) {
a[i] = {x: i, y: data[i]['cat' + (d+1)]};
}
return a;
})),
//the largest single layer
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
//the largest stack
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
var margin = {top: 40, right: 10, bottom: 20, left: 50},
width = 677 - margin.left - margin.right,
height = 533 - margin.top - margin.bottom;
var y = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([2, height], 0.08);
var x = d3.scale.linear()
.domain([0, yStackMax])
.range([0, width]);
var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return y(d.x); })
.attr("x", function(d) { return x(d.y0); })
.attr("height", y.rangeBand())
.attr("width", function(d) { return x(d.y); });
var yAxis = d3.svg.axis()
.scale(y)
.tickSize(1)
.tickPadding(6)
.tickValues(labels)
.orient("left");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
};
I think you are just missing a (); at the end of your function and before the semicolon. That would make it self executing. I forked your fiddle.
var VanillaRunOnDomReady = function() {
var margins = {
top: 12,
left: 48,
right: 24,
bottom: 24
};
var data = [
{"key":"Nonviolent", "cat1":0.69, "cat2":0.21, "cat3":0.10},
{"key":"Violent", "cat1":0.53, "cat2":0.29, "cat3":0.18}
];
var catnames = {cat1: "No mental illness",
cat2: "Mild mental illness",
cat3: "Moderate or severe mental illness"};
var svg = d3.select('body')
.append('svg')
.attr('width', width + margins.left + margins.right)
.attr('height', height + margins.top + margins.bottom)
.append('g')
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')');
var n = 3, // number of layers
m = data.length, // number of samples per layer
stack = d3.layout.stack(),
labels = data.map(function(d) {return d.key;}),
//go through each layer (cat1, cat2 etc, that's the range(n) part)
//then go through each object in data and pull out that objects's catulation data
//and put it into an array where x is the index and y is the number
layers = stack(d3.range(n).map(function(d) {
var a = [];
for (var i = 0; i < m; ++i) {
a[i] = {x: i, y: data[i]['cat' + (d+1)]};
}
return a;
})),
//the largest single layer
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
//the largest stack
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
var margin = {top: 40, right: 10, bottom: 20, left: 50},
width = 677 - margin.left - margin.right,
height = 533 - margin.top - margin.bottom;
var y = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([2, height], 0.08);
var x = d3.scale.linear()
.domain([0, yStackMax])
.range([0, width]);
var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var svg = d3.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return y(d.x); })
.attr("x", function(d) { return x(d.y0); })
.attr("height", y.rangeBand())
.attr("width", function(d) { return x(d.y); });
var yAxis = d3.svg.axis()
.scale(y)
.tickSize(1)
.tickPadding(6)
.tickValues(labels)
.orient("left");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
}();
Related
This error is:
not rendering properly
Error: attribute d: Expected number,"M0,NaNL890,NaN"
I am trying to create a D3js Burndown chart with ideal and actual data. but not able to display on the graph. how can i loop through "actual" and display data in y.domain and chart.append("path").datum(actual)
var margin = { top: 20, right: 20, bottom: 30, left: 50 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
ideal = [{ date: new Date(getyear, getmonth, getdate), points: noofhours }, { date: new Date(getyear1, getmonth1, getdate1), points: 0 }];
for (var i = 0; i < range.length - 1; i++) {
var validationItem = new Object();
validationItem["date"] = new Date(dayyear, daymonth, daydate);
validationItem["points"] = hours;
LstArray.push(validationItem);
}
actual = [LstArray];
actual.forEach(function (d1) {
d1.points = d1.points;
d1.date = d1.date;
});
var idealLine = d3.svg.line().x(function (d) { return x(d.date); }).y(function (d) { return y(d.points); });
var actualLine = d3.svg.line()
.x(function (d) {
var ar = [];
d.forEach(function (d1) {
//d.date = d.date;
ar.push(d1.date);
});
console.log('qqqq');
console.log(ar);
return ar;
})
.y(function (d) {
var ar = [];
d.forEach(function (d1) {
//d.date = d.date;
ar.push(d1.points);
});
console.log('ttt');
console.log(ar); return ar;
});
x.domain(d3.extent(ideal, function (d) { return d.date; })); // y.domain(d3.extent(d1.points, function (d) { return d.points; })); y.domain(d3.extent(actual, function (d) { return d1.points; }));
var xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(d3.time.format("%b %d"));
var yAxis = d3.svg.axis().scale(y).orient("left");
var chart = d3.select("#divBurnDownChart").append("svg")
.attr("class", "chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
chart.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")").call(xAxis);
// Create the y-axis
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Points");
// Paint the ideal line
chart.append("path")
.datum(ideal)
.attr("class", "line ideal")
.attr("d", idealLine);
// Paint the actual line
chart.append("path")
.datum(actual)
.attr("class", "line actual")
.attr("d", actualLine);
In a web application I was supposed to create a vertically grouped bar chart using d3.js using json data. Previously I create a horizontal grouped bar using the following code. Can anyone help me out? Thanks in advance.
var data = {
labels: [
'resilience', 'maintainability', 'accessibility',
'uptime', 'functionality', 'impact'
],
series: [
{
label: '2012',
values: [4, 8, 15, 16, 23, 42]
},
{
label: '2013',
values: [12, 43, 22, 11, 73, 25]
},
{
label: '2014',
values: [31, 28, 14, 8, 15, 21]
},]
};
var chartWidth = 300,
barHeight = 20,
groupHeight = barHeight * data.series.length,
gapBetweenGroups = 10,
spaceForLabels = 150,
spaceForLegend = 150;
// Zip the series data together (first values, second values, etc.)
var zippedData = [];
for (var i=0; i<data.labels.length; i++) {
for (var j=0; j<data.series.length; j++) {
zippedData.push(data.series[j].values[i]);
}
}
// Color scale
var color = d3.scale.category20();
var chartHeight = barHeight * zippedData.length + gapBetweenGroups * data.labels.length;
var x = d3.scale.linear()
.domain([0, d3.max(zippedData)])
.range([0, chartWidth]);
var y = d3.scale.linear()
.range([chartHeight + gapBetweenGroups, 0]);
var yAxis = d3.svg.axis()
.scale(y)
.tickFormat('')
.tickSize(0)
.orient("left");
// Specify the chart area and dimensions
var chart = d3.select(".chart")
.attr("width", spaceForLabels + chartWidth + spaceForLegend)
.attr("height", chartHeight);
// Create bars
var bar = chart.selectAll("g")
.data(zippedData)
.enter().append("g")
.attr("transform", function(d, i) {
return "translate(" + spaceForLabels + "," + (i * barHeight + gapBetweenGroups * (0.5 + Math.floor(i/data.series.length))) + ")";
});
// Create rectangles of the correct width
bar.append("rect")
.attr("fill", function(d,i) { return color(i % data.series.length); })
.attr("class", "bar")
.attr("width", x)
.attr("height", barHeight - 1);
// Add text label in bar
bar.append("text")
.attr("x", function(d) { return x(d) - 3; })
.attr("y", barHeight / 2)
.attr("fill", "red")
.attr("dy", ".35em")
.text(function(d) { return d; });
// Draw labels
bar.append("text")
.attr("class", "label")
.attr("x", function(d) { return - 10; })
.attr("y", groupHeight / 2)
.attr("dy", ".35em")
.text(function(d,i) {
if (i % data.series.length === 0)
return data.labels[Math.floor(i/data.series.length)];
else
return ""});
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + spaceForLabels + ", " + -gapBetweenGroups/2 + ")")
.call(yAxis);
// Draw legend
var legendRectSize = 18,
legendSpacing = 4;
var legend = chart.selectAll('.legend')
.data(data.series)
.enter()
.append('g')
.attr('transform', function (d, i) {
var height = legendRectSize + legendSpacing;
var offset = -gapBetweenGroups/2;
var horz = spaceForLabels + chartWidth + 40 - legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', function (d, i) { return color(i); })
.style('stroke', function (d, i) { return color(i); });
legend.append('text')
.attr('class', 'legend')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function (d) { return d.label; });
After continuous digging I found the correct way of doing this. Thanks to Mike Bostock for the example he provided in here. In here you can also find out the elaborate discussion of that example. Thanks for your support :)
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
//console.log(margin.left);
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 + ")");
/*Our json object is [{letter: "A", frequency: .08167,depth:.32},{letter: "B", frequency: .01492,depth:.69}]
To use csv file you just need to follow the link I provided
*/
var data = [
{letter: "A", frequency: .08167,depth:.32},
{letter: "B", frequency: .01492,depth:.69}
];
var groupNames=d3.keys(data[0]).filter(function(key){return key!="letter";})
data.forEach(function(d){
d.groups=groupNames.map(function(name){return {name:name,value:+d[name]};})
});
x0.domain(data.map(function(d){return d.letter;}));
x1.domain(groupNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0,d3.max(data,function(d){
return d3.max(d.groups,function(d){
return d.value;
});
})]);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Letter Fun");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.letter) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.groups; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
Please let me know if you have anything to know about the code.
I'm trying to render a stacked bar chart from a multidimensional array that is grouped together by stack, and I can't figure out how to configure the y and height values of each rectangle. The tutorial I've been going off is here, but their data structure varies greatly from mine. I believe the solution is in how the y0 and y1 values are determined, but being a novice in D3 I'm unable to discern how it's being computed in the tutorial.
Here's what I have so far (the returned data from the data and legendLabels variables is at the top:
data = [["Privés dans les trois dimensions","30.2"],["Privés dans une dimension additionelle","24.4"],["Privés seulement dans la dimension spécifiée","8.32"],["Privés dans les trois dimensions","30.2"],["Privés dans une dimension additionelle","26.1"],["Privés seulement dans la dimension spécifiée","3.75"],["Privés dans les trois dimensions","30.2"],["Privés dans une dimension additionelle","33.1"],["Privés seulement dans la dimension spécifiée","10.4"]]
legendLabels = ["Nutrition","Santé","Eau"];
var $chart = $(chart),
data = $chart.data("chartDataTest"),
legendLabels = $chart.data("chartLabels"),
groupedData = [];
$.each(legendLabels, function(i){
var labelLength = legendLabels.length,
dataLength = data.length;
var sliceBeginning = i * labelLength,
sliceEnd = sliceBeginning + labelLength;
dataRange = data.slice(sliceBeginning, sliceEnd);
groupedData.push(dataRange);
});
var yValues = [];
$.each(groupedData, function(i, dataGroup){
var subGroupY = 0;
$.each(dataGroup, function(ii, dataSubGroup){
subGroupY += +dataSubGroup[1];
});
yValues.push(subGroupY);
});
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 x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], 0.1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select(chart).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "parent-group")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(legendLabels.map(function(d) { return d; }));
x0.domain(groupedData.map(function(d) { return d; }));
y.domain([0, d3.max(yValues, function(d) { return d; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var group = svg
.selectAll(".group")
.data(groupedData)
.enter()
.append("g")
.attr("class", "g")
.attr("transform", function(d) {
return "translate(" + x0(d) + ",0)";
});
var yPosition = 0;
group
.selectAll(".group")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d, i) {
return y(d[1]);
})
.attr("height", function(d) {
return y(d[1]);
});
Here's what my chart looks like so far:
Per #CoolBlue's suggestion, I rerefactored this to use d3's stack function. I based my new approach on this tutorial. Here's the updated code:
var $chart = $(chart),
data = $chart.data("chartDataTest"),
chartStacks = $chart.data("chartLabels"),
groupedData = [],
colors = [];
var width = 900,
height = 500,
margin = { top: 10, right: 50, bottom: 30, left: 50},
x = d3.scale.ordinal().rangeRoundBands([0, width - margin.left - margin.right]),
x0 = d3.scale.ordinal().rangeRoundBands([0, width - margin.left - margin.right], 0.1),
y = d3.scale.linear().range([0, height - margin.top - margin.bottom]);
y0 = d3.scale.linear().range([height - margin.top - margin.bottom, 0]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom"),
yAxis = d3.svg.axis()
.scale(y0)
.orient("left")
.tickFormat(d3.format(".2s"));
$.each(chartStacks, function(i){
var labelLength = chartStacks.length,
dataLength = data.length;
var sliceBeginning = i,
dataRange = data.slice(sliceBeginning);
colorGroup = [];
$.each(dataRange, function(ii, colorData){
if ( ii % labelLength == 0) {
colorGroup.push(colorData[1]);
}
});
var remappedValues = colorGroup.map(function(dat, i) {
return {
x: i,
y: +dat
}
});
var remappedColors = dataRange.map(function(dat, i) {
return dat[0];
});
colors.push(remappedColors[0]);
groupedData.push(remappedValues);
});
var stacked = d3.layout.stack()(groupedData);
x0.domain(chartStacks.map(function(d) { return d; }));
x.domain(stacked[0].map(function(d) { return d.x; }));
y.domain([0, d3.max(stacked[stacked.length - 1], function(d) { return d.y0 + d.y; })]);
y0.domain([0, d3.max(data, function(d) { return 90; })]);
var svg = d3.select(chart).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + ", " + 46 + ")")
.call(yAxis);
var svgContent = svg
.append("g")
.attr("class", "parent-group")
.attr("transform", "translate(" + margin.left + "," + height + ")");
var valGroup = svgContent
.selectAll(".parent-group")
.data(stacked)
.enter()
.append("g")
.attr("class", "valgroup");
var rect = valGroup.selectAll("rect")
.data(function(d){return d;})
.enter().append("svg:rect")
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return -y(d.y0) - y(d.y); })
.attr("height", function(d) { return y(d.y); })
.attr("width", x.rangeBand() - 15);
I am working on a pictogram application.
Here is my latest code. I am going to attempt to place a texture layer on top of the rectangular blocks. Is there way of controlling the padding for the various axis?
var pictogramData = [
{
"label": "8",
"value": 8
},
{
"label": "9",
"value": 4
},
{
"label": "10",
"value": 9
},
{
"label": "11",
"value": 12
}
];
var margins = {
top: 0,
left: 30,
right: 24,
bottom: 0
};
var chart,
width = 300,
pixelGap = 2,
bar_height = 15,
height = ((bar_height + pixelGap) * pictogramData.length),
gapHeights = (pixelGap) * pictogramData.length;
svg = d3.select("#step-1")
.append('svg');
svg
.append('defs')
.append('pattern')
.attr('id', 'diagonalHatch')
.attr('patternUnits', 'userSpaceOnUse')
.attr('width', 4)
.attr('height', 4)
.append('path')
.attr('d', 'M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2')
.attr('stroke', '#000000')
.attr('stroke-width', 1);
chartWidth = width * 0.8;
chart = svg.append('g')
.attr('class', 'chart')
.attr('width', chartWidth)
.attr('height', height+gapHeights)
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')');
valueList = svg.append('g')
.attr('class', 'axis')
.attr('width', width *0.2)
.attr('transform', 'translate(' + (width - margins.right) + ',' + margins.top + ')')
chart
.append('g')
var x, y;
var max = d3.max(pictogramData, function(d) { return +d.value;} );
function getValueDomain(data){
var valueDomain = new Array();
for (i = 0; i < data.length; ++i) {
valueDomain.push(data[i].value);
}
return valueDomain;
}
var valueArray = getValueDomain(pictogramData);
x = d3.scale.linear()
.domain([0, max])
.range([0, chartWidth]);
y = d3.scale.ordinal()
.domain(valueArray)
.rangeBands([0, height]);
function plotRectangleGroups(groupName, pictogramData, chartWidth){
//Add a group to hold the rects
var group = chart.append("g")
.attr("class", groupName+"group");
group.selectAll("rect")
.data(pictogramData)
.enter().append("rect")
.attr("x", 0)
.attr("y", function(d, i){
return y(d.value) + (pixelGap*i);
})
.attr("width", function(d, i){
var barWidth = chartWidth;
if(
groupName != "base" &&
groupName != "pattern"
){
barWidth = x(d.value);
}
return barWidth;
})
.attr("height", y.rangeBand())
.attr('fill', function(){
var fill;
if(groupName == "pattern"){
fill = 'url(#diagonalHatch)';
}
return fill;
});
}
plotRectangleGroups("base", pictogramData, chartWidth);
plotRectangleGroups("rects", pictogramData, chartWidth);
plotRectangleGroups("pattern", pictogramData, chartWidth);
//left labels
labels = pictogramData.map(function (d) {
return d.label;
});
yScale = d3.scale.ordinal()
.domain(labels)
.rangeRoundBands([0, height]),
yAxis = d3.svg.axis()
.scale(yScale)
.orient('left'),
chart.append('g')
.attr('class', 'axis')
.call(yAxis);
//right labels
values = pictogramData.map(function (d) {
return d.value;
});
yScale = d3.scale.ordinal()
.domain(values)
.rangeRoundBands([0, height]),
yAxis = d3.svg.axis()
.scale(yScale)
.orient('right'),
valueList
.call(yAxis);
http://jsfiddle.net/4zt64yj8/18/
The misalignment of the bars and the tick labels in the Y axis comes from the pixelGap value.
pixelGap = 1,
...
.attr("y", function(d, i){
return y(d.value) + (pixelGap*i);
})
As you can see the pixelgap is used to add a little white space between the bars, but you (or whoever created the chart) forgot to compensate for them in the range. The pixelGap*i means that every next bar is pushed down a bit further, while the corresponding labels are not pushed down.
Simplest fix is to remove the multiplication and modify both the y and the height attribute:
group.selectAll("rect")
.data(pictogramData)
.enter().append("rect")
.attr("x", 0)
.attr("y", function(d, i){
return y(d.value) + pixelGap;
})
.attr("width", function(d, i){
var barWidth = chartWidth;
if(
groupName != "base" &&
groupName != "pattern"
){
barWidth = x(d.value);
}
return barWidth;
})
.attr("height", y.rangeBand() - pixelGap)
With this change the vertical padding is essentially equally spread out above and below the bar, which in turn gets rid of the misalignment.
I created a simple, updatedable d3.js bar chart.
Except for the x-axis, it works fine.
When I remove bars from the chart, the corresponding axis labels are not removed.
But when I remove bars for the second time, the labels of the bars that were removed in the first run are removed from the axis.
What is happening here, and why?
See a working demo here: http://jsfiddle.net/vACua/4/
The js code would be the following:
var BarChart = function (config) {
var data,
svg,
xScale,
yScale,
yAxis,
xAxis;
svg = d3.select('#' + config.targetElement).append('svg');
svg.attr("width", config.width + config.margin.left + config.margin.right)
.attr("height", config.height + config.margin.top + config.margin.bottom)
.attr("transform", "translate(" + config.margin.left + "," + config.margin.top + ")");
xScale = d3.scale.ordinal();
yScale = d3.scale.linear();
xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickSize(-config.height, 0).tickPadding(6);
svg.append('g').classed('x-axis-group axis', true);
svg.append("g")
.classed("x axis", true)
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// All methods and members defined here are public
return {
setData: function (newData) {
data = newData;
this.updateChart();
},
updateChart: function () {
var xDomain = data.map(function (d) {
return d.name;
}),
yDomain = data.map(function (d) {
return d.value;
});
xScale.domain(xDomain)
.rangeRoundBands([config.margin.left, config.width], 0.05);
yScale.domain([0, d3.max(data, function (d) {
return d.value;
})])
.range([config.height, 0]);
xAxis.scale(xScale)
.orient("bottom")
.tickSize(-config.height, 0)
.tickPadding(6);
var dataSections = svg.selectAll("g.bar")
.data(data, function (d) {
return d.name;
});
// adding new bars
dataSections.enter()
.append('g')
.attr('class', 'bar')
.append("rect")
.attr("height", 0);
var transition = svg.transition().duration(750);
transition.selectAll("g.bar").select('rect')
.attr("x", function (d, i) {
return xScale(d.name);
})
.attr("y", function (d) {
return yScale(d.value);
})
.attr("width", xScale.rangeBand())
.attr("height", function (d) {
return height - yScale(d.value);
});
dataSections.exit().transition().remove();
svg.select('.x.axis')
.transition()
.duration(500)
.attr({
transform: 'translate(0,' + (config.height) + ')'
})
.call(xAxis);
}
};
};
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
amount = 15;
function init() {
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 400 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
var chart = BarChart({
targetElement: 'foo',
margin: margin,
width: width,
height: height
});
setRandomData = function () {
var data = [];
for (var i = 0; i <= amount; i++) {
data.push({
name: "a" + i,
value: Math.floor((Math.random() * 300) + 1)
});
}
amount = amount - 5;
chart.setData(data);
}
}
init();
setRandomData();
setTimeout(setRandomData, 2000);
setTimeout(setRandomData, 4000);
Ok, I have absolutely no clue why but moving
svg.select('.x.axis')
.transition()
.duration(500)
.attr({transform: 'translate(0,' + (config.height) + ')'})
.call(xAxis);
before this line:
var transition = svg.transition().duration(750);
seems to work the way you intended: http://jsfiddle.net/Y274x/