In the previous section, we added the g elements and assigned those to the bars variable. In this section, we're going to calculate the width of the individual rectangles and add those and some text to the g:
var yScale = d3.scaleLinear()
.domain([0, d3.max(both, function (d) { return d.amount; })])
.range([0, width]);
bars.append('rect')
.attr("height", barWidth)
.attr("width", function (d) { return yScale(d.amount); })
.attr("class", function (d) { return d.sex === 'F' ? 'female' : 'male'; });
bars.append("text")
.attr("x", function (d) { return yScale(d.amount) - 5 ; })
.attr("y", barWidth / 2)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
Here we see something new: the d3.scaleLinear function. With a d3.scaleLinear, we can let D3 calculate how the number of times a name was given (the amount property) maps to a specific width. We want to use the full width (width property, which has a value of 720) of the chart for our bars, so that would mean that the highest value in our input data should map to that value:
- The name Emma, which occurred 20355 times, should map to a value of 720
- The name Olivia, which occurred 19553 times, should map to a value of 720 * (19553/20355)
- The name Mia, which occurred 14820 times, should map to a value of 720 * (14820/20355)
- And so on...
Now, we could calculate this ourselves and set the size of the rect accordingly, but using the d3.scaleLinear is much easier, and provides additional functionality. Let's look at the definition a bit closer:
var yScale = d3.scaleLinear()
.domain([0, d3.max(both, function (d) { return d.amount; })])
.range([0, width]);
What we do here, is we define a linear scale, whose input domain is set from 0 to the maximum amount in our data. This input domain is mapped to an output range starting at 0 and ending at width. The result, yScale, is a function which we can now use to map the input domain to the output range: for example, yScale(1234) returns 43.64922623434046.
Once you've got a scale, you can use a couple of functions to change its behavior:
Function |
Description |
invert(val) |
This function expects a value of the output domain, and returns the corresponding value from the input domain. |
rangeRound() |
You can use this instead of the range option we saw earlier. With this function, the scale only returns rounded values. |
clamp(bool) |
With the clamp function, you define the behavior of what happens when a value is passed in which is outside the input domain. In the case where clamp is true, the minimal or maximum output value is returned. In the case where clamp is false, an output value is calculated normally, which will result in a value outside the output domain. |
ticks([count]) |
This function returns a number of ticks (10 is the default), which can be used to create an axis, or reference lines. |
nice([ticks]) |
This function rounds the first and last value of the input domain. You can optionally specify a number of ticks you want to return, and the rounding function will take those into account. |
This is just a small part of the scales support provided by D3. In the rest of the book, we'll explore more of the scales options that are available.
With the scale defined, we can use that to create our rect and text elements in the same way we did in our previous example:
bars.append('rect')
.attr("height", barWidth)
.attr("width", function (d) { return yScale(d.amount); })
.attr("class", function (d) { return d.sex === 'F' ? 'female' : 'male'; });
Here we create a rect with a fixed height, and a width which is defined by the yScale and the number of times the name was used. We also add a class to the rect so that we can set its colors (and other styling attributes) through CSS. In the case where sex is F, we set the class female and in the other case we set the class male.
To position the text element, we do pretty much the same:
bars.append("text")
.attr("class", "label")
.attr("x", function (d) { return yScale(d.amount) - 5 ; })
.attr("y", barWidth / 2)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
We create a new text element, position it at the end of the bar, set a custom CSS class, and finally set its value to d.name. The dy attribute might seem a bit strange, but this allows us to position the text nicely in the middle of the bar chart. If we opened the example at this point, we'd see something like this:
We can see that all the information is in there, but it still looks kind of ugly. In the following section, we add some CSS to improve what the chart looks like.