I am new to d3 and having issue with showing the label of ordinal axis:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
<meta charset="utf-8">
<title>D3: Loading data from a CSV file</title>
</head>
<body>
<script type="text/javascript">
var margin = {top: 20, right: 20, bottom: 30, left: 40},
w = 600 - margin.left - margin.right,
h = 300 - margin.top - margin.bottom;
var padding = 40;
var data = [
{ "Food": "Apples", "Deliciousness": 9 },
{ "Food": "Green Beans", "Deliciousness": 5 },
{ "Food": "Egg Salad Sandwich", "Deliciousness": 4 },
{ "Food": "Cookies", "Deliciousness": 10 },
{ "Food": "Liver", "Deliciousness": 2 },
{ "Food": "Burrito", "Deliciousness": 7 },
];
data.forEach(function(d) {
d.Deliciousness = +d.Deliciousness;
});
var svg = d3.select("body")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left+"," +
margin.top+")");
var xScale = d3.scaleBand()
.domain(d=>d.Food)
.range([0,w])
.paddingInner(0.2);
xScale.domain(data.map(function(d) { return d.Food; }));
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5);
var yScale = d3.scaleLinear()
.domain([0, d3.max(data, d=>d.Deliciousness)])
.rangeRound([h,0]);
var yAxis = d3.axisLeft()
.scale(yScale)
.ticks(5);
svg.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('x',(d,i) => margin.left + i*w/data.length)
.attr('y',d=>yScale(d.Deliciousness))
.attr('width', xScale.bandwidth())
.attr('height',d =>h-yScale(d.Deliciousness))
.attr('fill',function(d){
if (d===30) return "red";
return "rgb(0,0,"+d.Deliciousness*10+")" ;});
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(d=>d.Deliciousness)
.attr("x", (d,i)=>(padding + i*w/data.length))
.attr("y", d=>yScale(d.Deliciousness)+15)
.attr("fill","white");
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h-margin.bottom) + ")")
.call(xAxis);
</script>
</body>
</html>
The x axis is somehow overlapping with the chart, how to properly use the margin?
And in terms of ordinal axis, other than list all the categories manually in .domain(), what are the other ways to special xScale in .domain().range() call? Thanks
It comes from the location definition of the x-axis.
You can change:
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h-margin.bottom) + ")")
.call(xAxis);
with:
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + margin.left + "," + h + ")")
.call(xAxis);
Here is the demo:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
<meta charset="utf-8">
<title>D3: Loading data from a CSV file</title>
</head>
<body>
<script type="text/javascript">
var margin = {top: 20, right: 20, bottom: 30, left: 40},
w = 600 - margin.left - margin.right,
h = 300 - margin.top - margin.bottom;
var padding = 40;
var data = [
{ "Food": "Apples", "Deliciousness": 9 },
{ "Food": "Green Beans", "Deliciousness": 5 },
{ "Food": "Egg Salad Sandwich", "Deliciousness": 4 },
{ "Food": "Cookies", "Deliciousness": 10 },
{ "Food": "Liver", "Deliciousness": 2 },
{ "Food": "Burrito", "Deliciousness": 7 },
];
data.forEach(function(d) {
d.Deliciousness = +d.Deliciousness;
});
var svg = d3.select("body")
.append("svg")
.attr("width", w + margin.left + margin.right + padding)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left+"," +
margin.top+")");
var xScale = d3.scaleBand()
.domain(d=>d.Food)
.range([0,w])
.paddingInner(0.2);
xScale.domain(data.map(function(d) { return d.Food; }));
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5);
var yScale = d3.scaleLinear()
.domain([0, d3.max(data, d=>d.Deliciousness)])
.rangeRound([h,0]);
var yAxis = d3.axisLeft()
.scale(yScale)
.ticks(5);
svg.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('x',(d,i) => margin.left + i * ((w + 20 ) / data.length))
.attr('y',d=>yScale(d.Deliciousness))
.attr('width', xScale.bandwidth())
.attr('height',d =>h-yScale(d.Deliciousness))
.attr('fill',function(d){
if (d===30) return "red";
return "rgb(0,0,"+d.Deliciousness*10+")" ;});
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(d=>d.Deliciousness)
.attr('x',(d,i) => margin.left + i * ((w + 20 ) / data.length))
.attr("y", d=>yScale(d.Deliciousness)+15)
.attr("fill","white");
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + margin.left + "," + h + ")")
.call(xAxis);
</script>
</body>
</html>
x-axis
location compared to its container (here svg
) is defined by the transform
attribute. Which in this case is a translation. To define a translation, we give to the transform attribute this value: translate(dx,dy)
.
dx: As rect
s are given a x offset of margin.left
(.attr('x',(d,i) => margin.left + i*w/data.length)
) we also need to translate the x-axis horizontally by margin.left
.
dy: And as rect
s have their base starting at h
(.rangeRound([h, 0]);
), we also need to translate the x-axis vertically by h
.
I have also modified the x position of bars and labels using:
.attr('x',(d,i) => margin.left + i * ((w + margin.right ) / data.length))
instead of:
.attr('x',(d,i) => margin.left + i*w/data.length)
Finally, as the last bar is half outside the graph, you can increase the svg container's width, by replacing:
.attr("width", w + margin.left + margin.right)
with:
.attr("width", w + margin.left + margin.right + padding)
Concerning your final question, this https://github.com/d3/d3-axis might give you additional details on how to use d3 axes.