Adding Label Text on Barchart D3js - javascript

I'm trying to figure out the correct way to displays labels that will sit on top of each bar in my bar chart. I'd also like them to display a % after the number.
Here is my Plunker: https://plnkr.co/edit/FbIquWxfLjcRTg7tiX4E?p=preview
I experimented with using this code from the question found in the link below. However, I wasnt able to get it to work properly (meaning the whole chart failed to display)
Adding label on a D3 bar chart
var yTextPadding = 20;
svg.selectAll(".bartext")
.data(data)
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "top")
.attr("fill", "white")
.attr("x", function(d,i) {
return x(i)+x.rangeBand()/2;
})
.attr("y", function(d,i) {
return height-y(d)+yTextPadding;
})
.text(function(d){
return d;
});

This is the most straight forward way given your existing code:
// keep a reference to the g holding the rects
var rectG = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; })
.selectAll("rect")
.data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
.enter();
// append the rects the same way as before
rectG.append("rect")
.attr("x", function(d) { return x1(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x1.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return z(d.key); });
// now add the text to the g
rectG.append("text")
.text(function(d){
return d.value + '%';
})
.attr("x", function(d) { return x1(d.key) + (x1.bandwidth()/2); })
.attr("y", function(d) { return y(d.value); })
.style("text-anchor", "middle");
Updated plunker.

Related

D3js group bar chart subgroups with 0 value

I'm trying to do something similar to this example but I'm passing a 0 value to one subgroup.
so I have a result like this.
is there a way to make one of the other subgroups have this blank space?, I thought it would be something like this.
svg.append("g")
.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) { return "translate(" + x(d.group) + ",0)"; })
.selectAll("rect")
.data(function(d) { return subgroups.map(function(key) { return {key: key, value: d[key]}; }); })
.enter().append("rect")
///.attr("x", function(d) { return xSubgroup(d.key); })//change this line to the next one
.attr("x", function(d) { if (d.value>0){return xSubgroup(d.key); }})
.attr("y", function(d) { return y(d.value); })
.attr("width", xSubgroup.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return color(d.key); });

How to label each bar from a grouped bar chart?

I'm newbie with D3.js and I have created a grouped bar chart. And I would like to put the value at the top of echa bar. But, I'm not able to do it :(
I have found several solutiones but I cannot do it works fine.
You can find all the code of my development here:
The function where I create all the bars is:
setBars(canvas, data, scales, keys, colors) {
let height = HEIGHT - OFFSET_TOP - OFFSET_BOTTOM;
let bar = canvas
.append("g")
.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + scales.x0Scale(d.shoot) + ",0)";
})
.selectAll("rect")
.data(function(d) {
return keys.map(function(key) {
return { key: key, value: d[key] };
});
})
.enter()
.append("rect")
.attr("class", "rect")
.attr("x", function(d) {
return scales.x1Scale(d.key);
})
.attr("y", function(d) {
return scales.yScale(d.value);
})
.attr("width", scales.x1Scale.bandwidth())
.attr("height", function(d) {
return height - scales.yScale(d.value);
})
.attr("fill", function(d) {
return colors(d.key);
});
//set label over bar
bar
.selectAll("g")
.data(function(d) {
return d.value;
})
.enter()
.append("text")
.attr("class", "bar-text")
.attr("fill", "#000")
.attr("text-anchor", "middle")
.text(function(d) {
console.log("d.value: " + d.value);
return d.value;
})
.attr("x", function(d, i) {
return scales.x1Scale.bandwidth() * (i + 0.5);
})
.attr("y", function(d, i) {
return scales.yScale(d.value) + 8;
});
}
What am I doing wrong?
If you look at the chain in your bar selection you'll see that you're trying to append text elements to rects: bar is a selection of rectangles, not a selection of groups. Appending texts to rectangles, of course, won't work in an SVG.
The solution is breaking that selection. For instance:
//This is the group selection:
let bar = canvas
.append("g")
.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + scales.x0Scale(d.shoot) + ",0)";
});
//Here, you append rects to the groups:
bar.selectAll("rect")
.data(function(d) {
return keys.map(function(key) {
return {
key: key,
value: d[key]
};
});
})
.enter()
.append("rect")
//etc...
//Finally, here, you append texts to the groups:
bar.selectAll("text")
.data(function(d) {
return keys.map(function(key) {
return {
key: key,
value: d[key]
};
});
})
.enter()
.append("text")
//etc...
As you can see, you'll have to change the data() method as well.

d3 bar chart labels not getting updated on updating the chart with the new data

Below is the code getting called everytime when I click on a button or an option in the drop down menu in order to update my d3 bar chart graph with the new data.
The bars in the chart are properly updated according to the new data, but the labels of the bar chart are not getting replaced with the new ones, instead both old and new labels are displayed.
Please suggest something so that old labels are not shown on bars.
function updateGraph(data) {
// scale the range of the data
x.domain(data.map(function(d) { return d.Country; }));
y.domain([0, d3.max(data, function(d) { return d.Value; })]);
var bars = svg.selectAll(".bar").data(data);
bars.enter().append("rect").attr("class", "bar");
bars.transition().duration(200).attr("x", function(d) { return x(d.Country); })
.attr("width", x.rangeBand())
.attr("y", function(d) {return y(d.Value); })
.attr("height", function(d) { return height - y(d.Value); });
var texts = svg.selectAll(".text").data(data);
texts.enter().append("text").attr("class","label").attr("x", (function(d) { return x(d.Country) + x.rangeBand() / 2 ; } ))
.attr("y", function(d) { return y(d.Value) + 1; })
.attr("dy", ".75em")
.text(function(d) { return d.Value; });
texts.transition().duration(200).attr("x", (function(d) { return x(d.Country) + x.rangeBand() / 2 ; } ))
.attr("y", function(d) { return y(d.Value) + 1; })
.attr("dy", ".75em")
.text(function(d) { return d.Value; });
bars.exit().remove();
svg.selectAll(".axis").remove();
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("Value");
}
You are not adding class to the text
This will select all DOM with class text
var texts = svg.selectAll(".text").data(data);
texts
.enter()
.append("text")
.attr("class","label text") <-- add text class here
.attr("x", (function(d) { return x(d.Country) + x.rangeBand() / 2 ; } ))
.attr("y", function(d) { return y(d.Value) + 1; })
.attr("dy", ".75em")
.text(function(d) { return d.Value; });
Now to remove all text on update
bars.exit().remove();
texts.exit().remove(); <--- add this to remove exited text

How to update a grouped bar-chart in d3js

In this plunker I've attempted to update the bars based on new csv data in a similar fashion to this.
It fetches the new data and updates the rectangles but doesn't remove the previous data.
I have no idea what part of the code isn't working right, in a previous attempt I omitted .attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; })
and got the leftmost group of bars to update and remove previous data but none of the other groups were showing.
Appreciate any help!
EDIT: Uploaded a working example here
Here's the relevant code:
let bars = g.selectAll(".test")
.data(data)
bars = bars.enter().append("g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; })
.selectAll("rect")
.data(function(d) {
return keys.map(function(key) {
return {key: key, value: d[key]};
});
})
bars = bars
.enter().append("rect")
.attr("width", x1.bandwidth())
.attr("x", function(d) { return x1(d.key); })
.attr("fill", function(d) { return z(d.key); })
.merge(bars)
bars.transition()
.duration(750)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
bars.exit().remove();
The layers/groups in the chart do not seem to update as per the new data. Here's some changes you could make to the bars' enter, update and exit code:
var barGroups = g.selectAll("g.layer").data(data);
barGroups.enter().append("g").classed('layer', true)
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; });
barGroups.exit().remove();
var bars = g.selectAll("g.layer").selectAll("rect")
.data(function(d) {
return keys.map(function(key) {
return {key: key, value: d[key]};
});
});
bars.enter().append("rect").attr("width", x1.bandwidth())
.attr("x", function(d) { return x1(d.key); })
.attr("fill", function(d) { return z(d.key); })
.transition().duration(750)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
bars
.transition().duration(750)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
bars.exit().remove();
So if you look at the code, bar groups/layers are being dealt with first and then the actual bars i.e. rectangles. Let me know if this doesn't work.
Hope this helps. :)

How to add numbers to bars with D3.js?

I've created a chart and it works fine, but I can't find out how to add numbers to columns. Numbers appear only if I hover the columns.
Tried different variants:
svg.selectAll("text").
data(data).
enter().
append("svg:text").
attr("x", function(datum, index) { return x(index) + barWidth; }).
attr("y", function(datum) { return height - y(datum.days); }).
attr("dx", -barWidth/2).
attr("dy", "1.2em").
attr("text-anchor", "middle").
text(function(datum) { return datum.days;}).
attr("fill", "white");
A link to my example: https://jsfiddle.net/rinatoptimus/db98bzyk/5/
Alternate idea to #gerardofurtado is to instead of a rect append a g, then group the text and rect together. This prevents the need for double-data binding.
var bars = svg.selectAll(".bar")
.data(newData)
.enter().append("g")
.attr("class", "bar")
// this might be affected:
.attr("transform", function(d, i) {
return "translate(" + i * barWidth + ",0)";
});
bars.append("rect")
.attr("y", function(d) {
return y(d.days);
})
.attr("height", function(d) {
return height - y(d.days) + 1;
})
.style({
fill: randomColor
}) // color bars
.attr("width", barWidth - 1)
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
bars.append("text")
.text(function(d) {
return d.days;
})
.attr("y", function(d) {
return y(d.days);
})
.attr("x", barWidth / 2)
.style("text-anchor", "middle");
Updated fiddle.
When you do this:
svg.selectAll("text")
you are selecting text elements that already exist in your SVG. In an enter selection, it's important selecting something that doesn't exist:
svg.selectAll(".text")
.data(newData)
.enter()
.append("svg:text")
.attr("x", function(datum) {
return x(datum.name) + x.rangeBand()/2
})
.attr("y", function(datum) {
return y(datum.days) - 10;
})
.attr("text-anchor", "middle")
.text(function(datum) {
return datum.days;
})
.attr("fill", "white");
Here is your fiddle: https://jsfiddle.net/c210osht/
You could try using the d3fc bar series component, which allows you to add data-labels via the decorate pattern.
Here's what the code would look like:
var svgBar = fc.seriesSvgBar()
.xScale(xScale)
.yScale(yScale)
.crossValue(function(_, i) { return i; })
.mainValue(function(d) { return d; })
.decorate(function(selection) {
selection.enter()
.append("text")
.style("text-anchor", "middle")
.attr("transform", "translate(0, -10)")
.text(function(d) { return d3.format(".2f")(d); })
.attr("fill", "black");
});
Here's a codepen with the complete example.
Disclosure: I am a core contributor to the d3fc project!

Categories

Resources