I want to build stacked chart bar.
To build it I am using d3js version 3.
I found some template on codepen and changed to my needs.
Bar Chart looks nice when I open it in Chrome:
But when I open it in Mozilla Firefox, it display with no colors:
What should I fix to make styles work?
Here is my html and javascript code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>d3.js learning</title>
<script data-require="d3#3.0.0" data-semver="3.0.0" src="https://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;
}
</style>
</head>
<body>
<script>
// Setup svg using Bostock's margin convention
/* Data in strings like it would be if imported from a csv */
var data = [
{ year: "2006", pid: "10", eid: "15"},
{ year: "2007", pid: "12", eid: "18"},
{ year: "2008", pid: "05", eid: "20"},
{ year: "2009", pid: "01", eid: "15"},
{ year: "2010", pid: "02", eid: "10"},
{ year: "2011", pid: "03", eid: "12"},
{ year: "2012", pid: "04", eid: "15"},
{ year: "2013", pid: "06", eid: "11"},
{ year: "2014", pid: "10", eid: "13"},
{ year: "2015", pid: "16", eid: "19"},
];
var margin = {top: 20, right: 160, bottom: 35, left: 30};
var width = 1000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
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 + ")");
var parse = d3.time.format("%Y").parse;
// Transpose the data into layers
var dataset = d3.layout.stack()(["pid", "eid"].map(function(id) {
return data.map(function(d) {
return {x: parse(d.year), y: +d[id]};
});
}));
// Set x, y and colors
var x = d3.scale.ordinal()
.domain(dataset[0].map(function(d) { return d.x; }))
.rangeRoundBands([10, width-10], 0.20);
var y = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) { return d3.max(d, function(d) { return d.y0 + d.y; }); })])
.range([height, 0]);
var colors = [ "3d3756", "574e7c"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat( function(d) { return d } );
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%Y"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");;
// Create groups for each series, rects for each segment
var groups = svg.selectAll("g.cost")
.data(dataset)
.enter().append("g")
.attr("class", "cost")
.style("fill", function(d, i) { return colors[i]; });
var rect = groups.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
.attr("width", x.rangeBand())
.on("mouseover", function() { tooltip.style("display", null); })
.on("mouseout", function() { tooltip.style("display", "none"); })
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
});
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(30," + i * 19 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {return colors.slice().reverse()[i];});
legend.append("text")
.attr("x", width + 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
switch (i) {
case 0: return "EID";
case 1: return "PID";
}
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
</script>
</body>
</html>
fixed it with adding hashes to colors:
from:
const all_colors = ["3d3756", "f0ee42", "f4c7de"];
to:
const all_colors = ["#3d3756", "#f0ee42", "#f4c7de"];
Related
I faced with a problem when some values in a bar very small when at the same time most of the other values are big enough. As the result these chunks with low values are almost not visible. I did not find any solution hot to correctly round chunks(not manually because I now that I can round them to more higher values via scale + invert(in order to determine what values I needed to show them more or less visible)). As an example below: as you see the last bar with low values is almost not visible. So can you suggest how to fix it? It would be great to have an opportunity to be able to specify the min size of stacked bar chart chunk. Thank you in advance.
http://jsfiddle.net/vhcdt13x/
// Setup svg using Bostock's margin convention
var margin = {top: 20, right: 160, bottom: 35, left: 30};
var width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
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 + ")");
/* Data in strings like it would be if imported from a csv */
var data = [
{ year: "2006", redDelicious: "100", mcintosh: "150", oranges: "90", pears: "60" },
{ year: "2012", redDelicious: "1", mcintosh: "1", oranges: "1", pears: "1" }
];
var parse = d3.time.format("%Y").parse;
// Transpose the data into layers
var dataset = d3.layout.stack()(["redDelicious", "mcintosh", "oranges", "pears"].map(function(fruit) {
return data.map(function(d) {
return {x: parse(d.year), y: +d[fruit]};
});
}));
// Set x, y and colors
var x = d3.scale.ordinal()
.domain(dataset[0].map(function(d) { return d.x; }))
.rangeRoundBands([10, width-10], 0.02);
var y = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) { return d3.max(d, function(d) { return d.y0 + d.y; }); })])
.range([height, 0]);
var colors = ["b33040", "#d25c4d", "#f2b447", "#d9d574"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat( function(d) { return d } );
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%Y"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Create groups for each series, rects for each segment
var groups = svg.selectAll("g.cost")
.data(dataset)
.enter().append("g")
.attr("class", "cost")
.style("fill", function(d, i) { return colors[i]; });
var rect = groups.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
.attr("width", x.rangeBand())
.on("mouseover", function() { tooltip.style("display", null); })
.on("mouseout", function() { tooltip.style("display", "none"); })
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
});
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(30," + i * 19 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {return colors.slice().reverse()[i];});
legend.append("text")
.attr("x", width + 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
switch (i) {
case 0: return "Anjou pears";
case 1: return "Naval oranges";
case 2: return "McIntosh apples";
case 3: return "Red Delicious apples";
}
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
I am making a line chart using the example here.
I am trying to change the axis tick label color but "fill" is not working. I don't want to change it using CSS, it has to be changed using D3.
I tried the following on the appropriate axis section:
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.attr("stroke", "yellow")
.style("fill", "yellow")
.style("font-size", "14px")
.call(d3.axisBottom(x));
The stroke and font-size change perfectly, but the fill has no effect. I tried changing .style to .attr but still nothing.
Here is the FIDDLE. Line 45 in the HTML I set it to green with no effect.
You want to change the color of x axis tick labels, so you need to select those labels and apply the fill value to it.
You can write the below code and accomplish your task, select all x-axis text and apply color to it.
d3.selectAll('g.axis--x text')
.style('fill', 'green')
Please find the complete working instance below:
data = [
{"year" : "2005", "value": 771900},
{"year" : "2006", "value": 771500},
{"year" : "2007", "value": 770500},
{"year" : "2008", "value": 770400},
{"year" : "2009", "value": 771000},
{"year" : "2010", "value": 772400},
{"year" : "2011", "value": 774100},
{"year" : "2012", "value": 776700},
{"year" : "2013", "value": 777100},
{"year" : "2014", "value": 779200},
{"year" : "2015", "value": 782300}
]
make_graph(data)
body {
background-color: #F1F3F3
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #D4D8DA;
stroke-width: 2px;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke: #6F257F;
stroke-width: 5px;
}
.overlay {
fill: none;
pointer-events: all;
}
.focus circle {
fill: #F1F3F3;
stroke: #6F257F;
stroke-width: 5px;
}
.hover-line {
stroke: #6F257F;
stroke-width: 2px;
stroke-dasharray: 3,3;
}
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<svg width="400" height="200"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
function make_graph(data) {
var svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var parseTime = d3.timeParse("%Y")
bisectDate = d3.bisector(function(d) {
return d.year;
}).left;
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var line = d3.line().x(function(d) {
return x(d.year);
}).y(function(d) {
return y(d.value);
});
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
data.forEach(function(d) {
d.year = parseTime(d.year);
d.value = +d.value;
});
x.domain(d3.extent(data, function(d) {
return d.year;
}));
y.domain([d3.min(data, function(d) {
return d.value;
}) / 1.005, d3.max(data, function(d) {
return d.value;
}) * 1.005]);
g.append("g")
.attr("class", "axis axis--x")
.style('font-family', 'Impact')
.style("fill", "green")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
d3.selectAll('g.axis--x text')
.style('fill', 'green')
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(6)
.tickFormat(function(d) {
return parseInt(d / 1000) + "k";
}))
.append("text")
.attr("class", "axis-title")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.attr("fill", "#5D6971")
.text("Population)");
g.append("path").datum(data)
.attr("class", "line")
.attr("d", line);
var focus = g.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("line")
.attr("class", "x-hover-line hover-line")
.attr("y1", 0)
.attr("y2", height);
focus.append("line")
.attr("class", "y-hover-line hover-line")
.attr("x1", width)
.attr("x2", width);
focus.append("circle")
.attr("r", 7.5);
focus.append("text")
.attr("x", 15)
.attr("dy", ".31em");
svg.append("rect")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "overlay").attr("width", width)
.attr("height", height)
.on("moseover", function() {
focus.style("display", null);
})
.on("mouseout", function() {
focus.style("display", "none");
})
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.year > d1.year - x0 ? d1 : d0;
focus
.attr("transform", "translate(" + x(d.year) + "," + y(d.value) + ")");
focus.select("text").text(function() {
return d.value;
});
focus.select(".x-hover-line").attr("y2", height - y(d.value));
focus.select(".y-hover-line").attr("x2", width + width);
}
}
</script>
I made a small change in your code.
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll('text')
.style("fill", "yellow")
Here is the jsFiddle
Hope it helps :)
I'd like to create a dot chart in d3.js, where it looks kind of like a bar chart, but there are stacked dots instead of bars.
This question is similar to what I want: D3.js (Wilkinson type) Dot Plot Example
I've used that code to try and accomplish what I want, but I'm stuck.
Here is an image showing what I want, based on the data for apples in my CSV file:
The code I have so far is below. When I console.log d3.range(d.apples), I see that I have an array for each day, which is what I want. However I can't figure out how to draw a circle for each spot in the array, where the x axis value would be the day # and the y axis value would be the position in the array + 1.
Any help very welcome! Thank you.
Code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #999;
shape-rendering: crispEdges;
}
.dot {
stroke: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("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 + ")");
var chart = svg.append("g")
.attr("id", "chart");
d3.csv("fruit.csv", function(error, data) {
if (error) throw error;
// console.log("data: ", data);
data.forEach(function(d) {
d.day = +d.day;
d.apples = +d.apples;
});
x.domain([0,4]);
y.domain([0,6]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Day");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("amount")
chart.selectAll("g")
.data(data)
.enter()
.append("g")
.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d, i) { return x(d.day); })
//.attr("cy", function(d,i){ return y(d.apples); })
.attr("cy", function(d,i,j){
var range = d3.range(d.apples);
console.log("apple range: ", range);
return y(range[i])
})
.style("fill", "blue")
.style("opacity", .5);
});
</script>
Data (fruit.csv):
"day","apples"
1,3
2,6
3,1
4,2
There are several ways to do what you want. I believe the most traditional approach is nesting the data and appending several groups, one for each fruit. But, for the sake of simplicity, I'd like to propose a different approach: let's change your data, flattening it, so, in each object, we have only one fruit:
originalData.forEach(function(d) {
data.push({
day: d.day,
fruit: "apples",
amount: +d.apples
});
data.push({
day: d.day,
fruit: "pears",
amount: +d.pears
});
data.push({
day: d.day,
fruit: "oranges",
amount: +d.oranges
});
});
Where originalData is the data array the way it is right now.
Doing that, your data will become something like this:
[{
"day": "1",
"fruit": "apples",
"amount": 3
}, {
"day": "1",
"fruit": "pears",
"amount": 6
}, {
"day": "1",
"fruit": "oranges",
"amount": 3
}, {
"day": "2",
"fruit": "apples",
"amount": 6
},//etc...
]
Then, we set a ordinal scale for the days:
var x = d3.scale.ordinal()
.rangePoints([0, width], 0.5);
.domain(data.map(d => d.day));
And, finally, we paint the circles:
var dots = svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) {
return x(d.day);
})
.attr("cy", function(d) {
return y(d.amount)
})
.style("fill", function(d) {
return color(d.fruit)
})
.style("opacity", .5);
Here is a demo (I put some titles, so you can hover over the dot to check the fruit and the amount):
var rawData = d3.csv.parse(d3.select("#csv").text());
var data = [];
rawData.forEach(function(d) {
data.push({
day: d.day,
fruit: "apples",
amount: +d.apples
});
data.push({
day: d.day,
fruit: "pears",
amount: +d.pears
});
data.push({
day: d.day,
fruit: "oranges",
amount: +d.oranges
});
});
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangePoints([0, width], 0.5);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10()
.domain(["apples", "pears", "oranges"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("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 + ")");
var chart = svg.append("g")
.attr("id", "chart");
x.domain(data.map(d => d.day));
y.domain([0, 10]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Day");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("amount");
var dots = svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) {
return x(d.day);
})
.attr("cy", function(d) {
return y(d.amount)
})
.style("fill", function(d) {
return color(d.fruit)
})
.style("opacity", .5)
.append("title")
.text(function(d){ return d.fruit + ": " + d.amount});
pre {
display: none;
}
body {
font: 10 px sans - serif;
}
.axis path,
.axis line {
fill: none;
stroke: #999;
shape-rendering: crispEdges;
}
.dot {
stroke: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<pre id="csv">"day","apples","pears","oranges"
1,3,6,3
2,6,8,2
3,1,0,7
4,2,3,8</pre>
EDIT: After further clarifications in the comments, this is what OP wants:
Append the groups as you did, but translating them according to the day:
var groups = svg.selectAll(".groups")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + x(d.day) + ".0)";
});
And, in the dots, set the data using d3.range:
var dots = groups.selectAll("circle")
.data(function(d){ return d3.range(+d.apples + 1)})
That way, for each group, the data for the circles goes all the way up to the maximum. For example, if in a given day "apples" is 3, the data will be:
[0, 1, 2, 3]
Or, if "apples" is 6, the data will be:
[0, 1, 2, 3, 4, 5, 6]
If you don't want the leading zero, just do d3.range(1, +d.apples + 1).
Here is a demo:
var data = d3.csv.parse(d3.select("#csv").text());
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangePoints([0, width], 0.5);
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");
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 + ")");
var chart = svg.append("g")
.attr("id", "chart");
x.domain(data.map(d => d.day));
y.domain([0, 7]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Day");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("amount");
var groups = svg.selectAll(".groups")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + x(d.day) + ".0)";
});
var dots = groups.selectAll("circle")
.data(function(d) {
return d3.range(1, +d.apples + 1)
})
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cy", function(d) {
return y(d)
})
.style("fill", "blue")
.style("opacity", .5);
pre {
display: none;
}
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #999;
shape-rendering: crispEdges;
}
.dot {
stroke: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<pre id="csv">"day","apples"
1,3
2,6
3,1
4,2</pre>
Trying to change the responsive stacked bar chart from vertical to horizontal but its not showing anything in the svg contained except the axis.
I have referred some of the similar problem in stack overflow like after changins stuff following some other examples from stackover flow like this and this.
Code for my vertical stacked bar chart in fiddle
But the current working code is here
margin = {
top: 20,
right: 160,
bottom: 35,
left: 30
};
var width = 600 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
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 + ")");
/* Data in strings like it would be if imported from a csv */
var data = [{
year: "A",
redDelicious: "10",
mcintosh: "5",
oranges: "19"
}, {
year: "B",
redDelicious: "12",
mcintosh: "0",
oranges: "15"
}, {
year: "C",
redDelicious: "05",
mcintosh: "0",
oranges: "28"
}, {
year: "D",
redDelicious: "14",
mcintosh: "0",
oranges: "12"
},
];
$("#btn").on("click", function(){
d3.selectAll("svg > g > g").remove();
data[1].mcintosh = (Number(data[1].mcintosh) + 1).toString();
console.log(1,data);
update();
});
update();
function update(){
var orangeData = data.map(function(d) {
return {
year: d.year,
oranges: +d.oranges
}
});
console.log(orangeData)
// Transpose the data into layers
var dataset = d3.layout.stack()(["redDelicious", "mcintosh"].map(function(skillset) {
return data.map(function(d) {
return {
y: d.year,
x: +d[skillset]
};
});
}));
console.log(dataset)
xMax = d3.max(dataset, function(group) {
return d3.max(group, function(d) {
return d.x + d.x0;
});
}),
xScale = d3.scale.linear()
.domain([0, xMax])
.range([0, width]);
months = dataset[0].map(function(d) { return d.y; });
yScale = d3.scale.ordinal()
.domain(months)
.rangeRoundBands([0, height], .1);
// Set x, y and colors
/* var y = d3.scale.ordinal()
.domain([0, d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y;
});
})])
.rangeRoundBands([height, 0], 0.02);
var x = d3.scale.linear()
.domain(dataset[0].map(function(d) {
return d.x0 + d.x;
}))
.range([10, width-10]); */
var colors = ["#b33040", "#d9d574"];
var backcolors = ["red", "blue","green","pink"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom").ticks(5)
.tickSize(-width, 0, 0);
/* .tickFormat(function(d) {
return 0
});*/
// .tickFormat(d3.time.format("%Y"));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis).attr("transform", "rotate(-0)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
// Creating the Average Bar for the Semester
svg.selectAll(".bar1").data(orangeData).enter().append("g")
.attr("class", "bar1").append("rect")
.attr("y", function(d) {
return y(d.year) ; // center it
})
.attr("width", x.rangeBand()) // make it slimmer
.attr("x", function(d) {
return x(d.oranges);
})
.attr("width", function(d) {
return width - x(d.oranges);
});
// Create groups for each series, rects for each segment in Stacked Bar
var groups = svg.selectAll("g.cost")
.data(dataset)
.enter().append("g")
.attr("class", "cost")
.style("fill", function(d, i) {
return colors[i];
});
var rect = groups.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("y", function(d,y) {
return yScale(d.y) + 20 ;
})
.attr("x", function(d) {
return xScale(d.x0 );
})
.attr("width", function(d) {
return xScale(d.x);
})
.attr("height", yScale.rangeBand() -40 );
}
The problem:
I'm new to D3.js and I've created a bar chart that gets data from an array. I placed text on top of the bars and the text is wider than the bars themselves. I want to rotate it vertical so that the text goes through the middle of the bar.
I know .attr("transform", "rotate(180)") won't work because it rotates my text off-screen. I found an answer from Mike Bostock here, but it doesn't seem to work for me, I don't know what I'm doing wrong. He says I need to change the rotation point of origin, but I can't get it to work.
Any help is appreciated, you can find the code below:
var label = svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d){
return d;
})
.attr("x", function(d, i){
xText = i*(w/data.length);
return xText;
})
.attr("y", function(d){
yText = h - yScale(d);
return yText;
})
.attr("transform", "translate(" + xText + "," + yText + ") rotate(90)")
.attr("fill", "black")
.attr("font-family", "sans-serif")
.attr("font-size", "11px");
Thanks!
You are setting both the x and y attributes and translated the text. With the rotate, you only want to use the translate:
.attr("transform", function(d,i){
var xText = i * (w / data.length);
var yText = h - yScale(d.v);
return "translate(" + xText + "," + yText + ") rotate(90)";
})
Also, I find your example code confusing, I was assuming that your data was an array of strings since you do:
.text(function(d) {
return d;
})
but that would make yScale(d), return NaN.
Here's an example that works nicely:
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>
<body>
<script>
var w = 300,
h = 300,
data = [{
t: "Now",
v: 50
},{
t: "is",
v: 25
},{
t: "the",
v: 10
},{
t: "winter",
v: 30
}];
var svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h);
var yScale = d3.scale.linear()
.range([0, h])
.domain([60, 0]);
var label = svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d) {
return d.t;
})
.attr("transform", function(d,i){
var xText = i * (w / data.length);
var yText = h - yScale(d.v);
return "translate(" + xText + "," + yText + ") rotate(90)";
})
.attr("fill", "black")
.attr("font-family", "sans-serif")
.attr("font-size", "11px");
</script>
</body>
</html>
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var data = [{
"letter": "A",
"frequency": 0.08167
}, {
"letter": "B",
"frequency": 0.01492
}, {
"letter": "C",
"frequency": 0.02782
}, {
"letter": "D",
"frequency": 0.04253
}, {
"letter": "E",
"frequency": 0.12702
}];
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 500 - margin.left - margin.right,
height = 200 - 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 + ")");
x.domain(data.map(function(d) {
return d.letter;
}));
y.domain([0, d3.max(data, function(d) {
return d.frequency;
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
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("Frequency");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.letter);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.frequency);
})
.attr("height", function(d) {
return height - y(d.frequency);
});
function type(d) {
d.frequency = +d.frequency;
return d;
}
var text = svg.selectAll(".text").data(data);
text.enter()
.append("text")
.attr("class", "text")
.attr("x", function(d, i) {
return x(d.letter);
})
.attr("y", function(d) {
return y(d.frequency);
})
.text(function(d) {
return d.letter;
})
.attr("transform", function(d) {
return "rotate(90 " + x(d.letter) + "," + (y(d.frequency) - d.letter.length) + ")"
});
</script>
You can try out this, and as a base you can modify this as per your need. I hope it helps.