Related
How to rotate text of label of barchart in d3.js? I tried using
.attr("transform", "rotate(-90)") but it rotates the entire column.
csv file:
date,value
2013-01,53
2013-02,165
2013-03,269
2013-04,344
2013-05,376
2013-06,410
2013-07,421
2013-08,405
2013-09,376
2013-10,359
2013-11,392
2013-12,433
2014-01,455
2014-02,478
code:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
// Code goes here
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
padding = -60; // space around the chart, not including labels
// Parse the date / time
var parseDate = d3.timeParse("%Y-%m");
var x = d3.scaleBand().rangeRound([0, width]).paddingInner(0.05);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom(x)
.tickFormat(d3.timeFormat("%Y-%m"));
var yAxis = d3.axisLeft(y)
.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 + ")");
d3.csv("bar-data.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value = +d.value;
});
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
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", "-.55em")
.attr("transform", "rotate(-90)" );
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("Value ($)");
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
svg.selectAll(".text")
.data(data)
.enter()
.append("text")
//.attr("transform", "rotate(-90)")
.attr("x", (function(d) { return x(d.date); } ))
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("dy", ".75em")
.text(function(d) { return d.value; });
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d) {
return d.value;
})
.attr("text-anchor", "middle")
.attr("fill", "white")
.attr("x", function(d, i) {
return i * (width / data.length);
})
.attr("y", function(d) {
return height - (d * 4);
});
});
// now add titles to the axes
svg.append("text")
.attr("text-anchor", "middle") // this makes it easy to centre the text as the transform is applied to the anchor
.attr("transform", "translate("+ (padding/2) +","+(height/2)+")rotate(-90)") // text is drawn off the screen top left, move down and out and rotate
.text("Value");
svg.append("text")
.attr("text-anchor", "middle") // this makes it easy to centre the text as the transform is applied to the anchor
.attr("transform", "translate("+ (width/2) +","+(height-(padding))+")") // centre below axis
.text("Date");
</script>
</body>
</html>
What you see is actually the expected behaviour, since the rotate function of the transform attribute rotates all the elements around their origins, not around their centres.
The easiest solution is passing the optional x and y arguments to rotate:
.attr("transform", function(d) {
return "rotate(-90," + x(d.date) + "," + y(d.value) + ")";
//x and y here ------------^------------------^
})
Here is your code with that change:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
// Code goes here
var csv = `date,value
2013-01,53
2013-02,165
2013-03,269
2013-04,344
2013-05,376
2013-06,410
2013-07,421
2013-08,405
2013-09,376
2013-10,359
2013-11,392
2013-12,433
2014-01,455
2014-02,478`;
var margin = {
top: 20,
right: 20,
bottom: 70,
left: 40
},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
padding = -60; // space around the chart, not including labels
// Parse the date / time
var parseDate = d3.timeParse("%Y-%m");
var x = d3.scaleBand().rangeRound([0, width]).paddingInner(0.05);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom(x)
.tickFormat(d3.timeFormat("%Y-%m"));
var yAxis = d3.axisLeft(y)
.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 + ")");
var data = d3.csvParse(csv);
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value = +d.value;
});
x.domain(data.map(function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.value;
})]);
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", "-.55em")
.attr("transform", "rotate(-90)");
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("Value ($)");
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) {
return x(d.date);
})
.attr("width", x.bandwidth())
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
});
svg.selectAll(".text")
.data(data)
.enter()
.append("text")
.attr("transform", function(d) {
return "rotate(-90," + x(d.date) + "," + y(d.value) + ")";
})
.attr("x", (function(d) {
return x(d.date);
}))
.attr("width", x.bandwidth())
.attr("y", function(d) {
return y(d.value);
})
.attr("dy", "1.3em")
.attr("dx", "0.2em")
.text(function(d) {
return d.value;
});
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d) {
return d.value;
})
.attr("text-anchor", "middle")
.attr("fill", "white")
.attr("x", function(d, i) {
return i * (width / data.length);
})
.attr("y", function(d) {
return height - (d * 4);
});
// now add titles to the axes
svg.append("text")
.attr("text-anchor", "middle") // this makes it easy to centre the text as the transform is applied to the anchor
.attr("transform", "translate(" + (padding / 2) + "," + (height / 2) + ")rotate(-90)") // text is drawn off the screen top left, move down and out and rotate
.text("Value");
svg.append("text")
.attr("text-anchor", "middle") // this makes it easy to centre the text as the transform is applied to the anchor
.attr("transform", "translate(" + (width / 2) + "," + (height - (padding)) + ")") // centre below axis
.text("Date");
</script>
</body>
</html>
I'm relatively new to D3 and trying to add labels to a grouped bar chart.. With below implementation, I'm only able to see them under one group instead of both.
Below is how the data looks in the db:
category,Exceed,Fully Meets,Partially Meets,Does not meet
business,10,20,30,30
leadership,15,5,30,50
Below is the code:
var chart1 = d3.select("#svgarea2"),
margin = { top: 70, right: 0, bottom: 30, left: 40 },
width = +chart1.attr("width") - margin.left - margin.right,
height = +chart1.attr("height") - margin.top - margin.bottom,
g = chart1.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//chart background color
var bg = d3.select("g").append("svg")
// .attr("width", width + margin.right + margin.left)
.attr("width", 510 + "px")
// .attr("height", height + margin.top + margin.bottom);
.attr("height", 310 + "px");
bg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "#f8f8ff");
bg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//scale chart
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.4);
var x1 = d3.scaleBand()
.padding(0.05);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal() //d3.schemeCategory20
// .range(["#0000ff", "#dcdcdc", "#696969", "#00008b"]);
.range(["#00008b", "#696969", "#dcdcdc", "#0000ff"]);
var columns = ['category', 'Does Not Meet', 'Partially Meets', 'Fully Meets', 'Exceed'];
var keys = columns.slice(1);
var color = d3.scaleOrdinal()
.range(["#00008b", "#696969", "#dcdcdc", "#0000ff"]);
x0.domain(data.map(function (d) { return d.category; }));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, 100]).nice();
g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function (d) { return "translate(" + x0(d.category) + ",0)"; })
.selectAll("rect")
.data(function (d) { return keys.map(function (key) { return { key: key, value: d[key] }; }); })
.enter().append("rect")
.attr("x", function (d) { return x1(d.key); })
.attr("y", function (d) {
return y(d.value);
})
.attr("width", x1.bandwidth() - 7)
.attr("height", function (d) { return height - y(d.value); })
.attr("fill", function (d) { return z(d.key); })
.on("mousemove", function(d){
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html((d.key) + "<br>" + (d.value) + "%");
});
g.append("g")
.selectAll("g")
.data(data).enter()
.append("g")
.attr("transform", function (d) { return keys })
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.attr("x", function (d) { return x0(d.category); })
.call(d3.axisBottom(x1))
.selectAll("text")
.attr("y", 15)
.attr("x", 0)
.attr("dy", ".35em")
.attr("transform", "rotate(50)")
.style("text-anchor", "start");;
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) - 5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Employees (%)");
//chart title
g.append("text")
.attr("x", (width / 2) + 30)
.attr("y", 1 - (margin.top / 2) + 20)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("font-weight", "bold")
.style("text-decoration", "underline")
.attr("font-family", "sans-serif")
.text("Performance Distribution");
};
Any help is appreciated!
It will work if you create a g element for each barchart, and then add you axes, bars etc to each. See here for example:
http://blockbuilder.org/tomshanley/aa5471a3ecaf9e41283d68188aecf042
Relevant code:
var chart = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g");
chart.attr("transform", function (d) { return "translate(" + x0(d.category) + ",0)"; })
.selectAll("rect")
.data(function (d) { return keys.map(function (key) { return { key: key, value: d[key] }; }); })
.enter().append("rect")
.attr("x", function (d) { return x1(d.key); })
.attr("y", function (d) {
return y(d.value);
})
.attr("width", x1.bandwidth() - 7)
.attr("height", function (d) { return height - y(d.value); })
.attr("fill", function (d) { return z(d.key); })
//APPEND AN AXIS TO THE CHART G'S
chart.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.attr("x", function (d) { return x0(d.category); })
.call(d3.axisBottom(x1))
.selectAll("text")
.attr("y", 15)
.attr("x", 0)
.attr("dy", ".35em")
.attr("transform", "rotate(50)")
.style("text-anchor", "start");
I'm trying to add labels to my bar chart, and I can't make it work. I read a lot about it and tried many things already, but no success.
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var parseDate = d3.timeParse("%Y-%m");
var x = d3.scaleBand().rangeRound([0, width]).paddingInner(0.05);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom(x)
.tickFormat(d3.timeFormat("%Y-%m"));
var yAxis = d3.axisLeft(y)
.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 + ")");
d3.csv("bar-data.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value = +d.value;
});
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
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", "-.55em")
.attr("transform", "rotate(-90)" );
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("Value ($)");
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d) {
return d.value;
})
.attr("text-anchor", "middle")
.attr("fill", "white")
.attr("x", function(d, i) {
return i * (width / data.length);
})
.attr("y", function(d) {
return height - (d * 4);
});
});
Here is my Plunker.
Any help is appreciated. Thank you in advance.
You can add labels to the bars using the snippet below -
svg.selectAll(".text")
.data(data)
.enter()
.append("text")
.attr("class","label")
.attr("x", (function(d) { return x(d.date); } ))
.attr("y", function(d) { return y(d.value) - 20; })
.attr("dy", ".75em")
.text(function(d) { return d.value; });
Your problem was here. The correct approach to get x-y positions for the labels would be the same approach you used for the bars using the x and y variables.
.attr("x", function(d, i) {
return i * (width / data.length);
})
.attr("y", function(d) {
return height - (d * 4);
});
Updated Plunkr - https://plnkr.co/edit/e6HPuph3OSbpxeEVQXfF?p=preview
I usually make a line legend by appending a rect giving it a very small height so that it looks like a line.
But now I need a dashed line legend. I am not able to do it by my old way. Can anyone show me a quick example of how to make a line legend with append('path') with d3.js?
You can make it like this with a line DOM:
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())//data set for legends
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("line")//making a line for legend
.attr("x1", width - 28)
.attr("x2", width)
.attr("y1", 10)
.attr("y2", 10)
.style("stroke-dasharray","5,5")//dashed array for line
.style("stroke", color);
legend.append("text")
.attr("x", width - 44)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
Working example here
Hope this work
Earlier i was appending .text element to g tag to show the text. But now as length of text is more than one line i am trying to follow below example and put div in g tag.
But it's not working Below is the Code. Am i missing something?.
var width = 960,
height = 500;
var w=300,h=300;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
d3.json("data.json", function (json) {
/* Define the data for the circles */
var elem = svg.selectAll("g myCircleText")
.data(json.nodes)
/*Create and place the "blocks" containing the circle and the text */
var elemEnter = elem.enter()
.append("g")
.attr("transform", function (d) { return "translate(" + d.x + ",180)" })
/*Create the circle for each block */
var circle = elemEnter.append("circle")
.attr("r", function (d) { return d.r })
.attr("stroke", "black")
.attr("fill", "white")
.style("fill", "url(#image)")
.on("click", function (d) {
var g = svg.append("g")
.attr("transform", "translate(150,100)");
g.append("rect")
.attr("width", 200)
.attr("height", 100)
.style("fill", "#E6EFFA")
.transition()
.duration(750)
.attr("width", 500)
.attr("height", 400)
.each("end", function () {
var tempcircle = g.append("circle")
.attr("r", "50")
.attr("stroke", "black")
.attr("fill", "blue")
.style("fill", "url(#image)");
g.append("text")
.attr("dx", "200")
.attr("dy", "30")
.text(d.label);
var templine = g.append("line")
.attr("x1", 20)
.attr("y1", 50)
.attr("x2", 450)
.attr("y2", 50)
.attr("stroke-width", 2)
.attr("stroke", "white");
g.append("text")
.attr("dx", "180")
.attr("dy", "80")
.text(d.info);
g.append("text")
.attr("dx", "120")
.attr("dy", "120")
.style("font-weight", "bold")
.text(d.first);
g.append("div")
.attr("id", "div_01")
.style({ width: w + "px", height: h + "px" })
.attr("dx", "150")
.attr("dy", "150")
.text(d.Answer);
g.append("text")
.attr("dx", "120")
.attr("dy", "170")
.style("font-weight", "bold")
.text(d.second);
g.append("text")
.attr("dx", "150")
.attr("dy", "190")
.text(d.A2);
g.append("text")
.attr("dx", "120")
.attr("dy", "220")
.style("font-weight", "bold")
.text(d.third);
g.append("text")
.attr("dx", "150")
.attr("dy", "240")
.text(d.A3);
var templine1 = g.append("line")
.attr("x1", 20)
.attr("y1", 270)
.attr("x2", 450)
.attr("y2", 270)
.attr("stroke-width", 2)
.attr("stroke", "white");
g.append("circle")
.attr("r", "15")
.attr("cx", "505")
.attr("cy", "6")
.style("fill", "#B5CEE7")
.on('click', function () {
d3.selectAll("rect").remove();
d3.selectAll("text").remove();
d3.select(this).remove();
tempcircle.remove();
templine.remove();
templine1.remove();
})
g.append("text")
.attr("dx", "500")
.attr("dy", "8")
.text("x");
})
});
})
There is a div appending to g tag.
http://bl.ocks.org/JohnDelacour/5676636
is the example i am trying to follow.
Thanks
You can't add html to svg unless you use foreignObject. The example you show keeps the divs and the svg separated and superimposed. It's a modification of my foreignObject example: http://tributary.io/inlet/5320723