score:1

Accepted answer

The datum of each rectangle includes all the bars in that column in a data property:

enter image description here

The datum also includes the top and bottom (start and end) values for the rectangle itself: d[0] and d[1].

Using this we can figure out a percentage:

.on("mouseover", function(d) { 
   var rectHeight = d[1] - d[0]; // The top and bottom vales of the rectangle.
   var columnHeight = d3.sum(d3.keys(d.data),function(k) {
      return +d.data[k]; // The sum of all bar rectangle heights in that column.
  });
  var percentHeight = rectHeight/columnHeight*100;

The units for rect height and column height above aren't SVG units, but are in the units of the dataset since we are accessing the data directly without scaling.

d3.sum will ignore NaN or undefined values, a unary plus operator will coerce a string to a number, if this is not possible it will return NaN. Of course, if you have a month or x attribute that is a number, you'll need to remove it from the sum

With this we get something like:

<!DOCTYPE html>
<meta charset="utf-8">
<svg id="chart" width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

var data = [
  {month: "January", toys: 16591, games: 1047, books: 0, crafts: 5757},
  {month: "February", toys: 42337, games: 129, books: 835, crafts: 0},
  {month: "March", toys:  3385, games: 1053, books: 6260, crafts: 10},
  {month: "April", toys:  353, games: 3724, books: 4038, crafts: 0}
];

var series = d3.stack()
    .keys(["toys", "games", "books", "crafts"])
    .offset(d3.stackOffsetDiverging)
    (data);

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

var x = d3.scaleBand()
    .domain(data.map(function(d) { return d.month; }))
    .rangeRound([margin.left, width - margin.right])
    .padding(0.1);

var y = d3.scaleLinear()
    .domain([d3.min(series, stackMin), d3.max(series, stackMax)])
    .rangeRound([height - margin.bottom, margin.top]);

var z = d3.scaleOrdinal(d3.schemeCategory10);

var colors = ['#000','#000','#000','#000'];

svg.append("g")
  .selectAll("g")
  .data(series)
  .enter().append("g")
    .attr("fill", function(d) { return z(d.key); })
  .selectAll("rect")
  .data(function(d) { return d; })
  .enter().append("rect")
    .attr("width", x.bandwidth)
    .attr("x", function(d) { return x(d.data.month); })
    .attr("y", function(d) { return y(d[1]); })
    .attr("height", function(d) { return y(d[0]) - y(d[1]); })
    .on("mouseover", function(d){
        var rectHeight = d[1] - d[0];
        var columnHeight = d3.sum(d3.keys(d.data),function(k) {
           return +d.data[k];
        });
        console.log(Math.round(rectHeight/columnHeight*100)+"%");
    })

svg.append("g")
    .attr("transform", "translate(0," + y(0) + ")")
    .call(d3.axisBottom(x));

svg.append("g")
    .attr("transform", "translate(" + margin.left + ",0)")
    .call(d3.axisLeft(y));

function stackMin(serie) {
  return d3.min(serie, function(d) { return d[0]; });
}

function stackMax(serie) {
  return d3.max(serie, function(d) { return d[1]; });
}

</script>


Related Query