Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3v4 Stacked Barchart Tooltip

I'm using d3.stack() to create a normalized stacked barchart.
But I'm having trouble accessing the relevant values of the inital dataset for a mousover tooltip.

serie.selectAll("rect")
    .data(function(d) { return d; })
    .enter().append("rect")
      ...
      .on("mousemove", function(d){
          let coords = d3.mouse(svg.node());
          tooltip.style("left", coords[0] + "px");
          tooltip.style("top", coords[1] - 70 + "px");
          tooltip.style("display", "inline-block");
          tooltip.html("HOW TO ACCESS DATA HERE?");
        });

d is the Array[2] at this point with the values defining baseline / topline, d.data is the complete original data object but is missing the information over which stack of the series I'm currently hovering.
Ideally I want the tooltip to show for a dataset like {name:"item1", foo:10, bar:20}

value: 10
percentage: 33%

when hovering above the foo rect.
All the examples I've found are for D3v3 where you can simply use d.y to access the relevant value but that doesn't seem to work any more with D3v4.

like image 975
TommyF Avatar asked Feb 06 '23 03:02

TommyF


1 Answers

You can get the name of the element accessing the parent's data, and using that name to get the value:

.on('mouseover', function(d, i) {
    var thisName = d3.select(this.parentNode).datum().key;
    var thisValue = d.data[thisName];
    var total = d.data.foo + d.data.bar;
    tooltip.html("Name: " + thisName + "<br>Value: " + thisValue + "<br>Percentage: " + thisValue / total + "%");
});

Here is your code with that modification:

var svg = d3.select("svg"),
    margin = {
        top: 20,
        right: 60,
        bottom: 30,
        left: 40
    },
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var x = d3.scaleBand()
    .rangeRound([0, width])
    .padding(0.1)
    .align(0.1);

var y = d3.scaleLinear()
    .rangeRound([height, 0]);
var stack = d3.stack()
    .offset(d3.stackOffsetExpand);

var colors = ["#FF0000", "#00FF00", "#0000FF"];

var data = [{
    name: "item1",
    foo: 10,
    bar: 20
}, {
    name: "item2",
    foo: 50,
    bar: 50
}];

x.domain(data.map(function(d) {
    return d.name;
}));

var serie = g.selectAll(".serie")
    .data(stack.keys(["foo", "bar"])(data))
    .enter().append("g")
    .attr("class", "serie")
    .attr("fill", function(d, i) {
        return colors[i];
    });


var tooltip = d3.select('#tooltip');

var rects = serie.selectAll("rect")
    .data(function(d) {
        return d;
    })
    .enter().append("rect")

rects.attr("x", function(d) {
        return x(d.data.name);
    })
    .attr("y", function(d) {
        return y(d[1]);
    })
    .attr("height", function(d) {
        return y(d[0]) - y(d[1]);
    })
    .attr("width", x.bandwidth())
    .on('mouseover', function(d, i) {
        var thisName = d3.select(this.parentNode).datum().key;
        var thisValue = d.data[thisName];
        var total = d.data.foo + d.data.bar;
        tooltip.html("Name: " + thisName + "<br>Value: " + thisValue + "<br>Percentage: " + thisValue / total + "%");
    });

g.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x));

g.append("g")
    .attr("class", "axis axis--y")
    .call(d3.axisLeft(y).ticks(10, "%"));
<script src="https://d3js.org/d3.v4.min.js"></script>

<div id="tooltip">tooltip</div>
<svg width="500" height="300"></svg>
like image 55
Gerardo Furtado Avatar answered Feb 07 '23 18:02

Gerardo Furtado