curiousguy curiousguy - 6 months ago 37
Javascript Question

d3.js group bar chart not working

Here is my working jsfiddle https://jsfiddle.net/dibyendu/wn6awvbe/ . Now this bar chart is working fine for

single value
and my requirement is to make it
group bar chart
.

So do this as a first step I have to create x-axis range based on
min
and
max
of all the values. I am able to do this successfully .

Second I tried to create a
group
and add individual
group bars
into the
group
. This part I am not able to crack.

Here is my code what I have tried

var combos = svg.selectAll("g").data(data)
.enter()
.append("g")
.attr({width: 2 * width})
.attr('height',y.rangeBand()+10)
;

combos.selectAll(".bar1")
.append("rect")
.attr("class", function(d) { return "bar bar--" + (d.value < 0 ? "negative" : "positive"); })
.attr("x", function(d) { return x(Math.min(0, d.value)); })
.attr("y", function(d) { return y(d.name); })
.attr("width", function(d) { return Math.abs(x(d.value) - x(0)); })
.attr("height", y.rangeBand());

combos.selectAll(".bar2")
.append("rect")
.attr("class", function(d) { return "bar bar--" + (d.value < 0 ? "negative" : "positive"); })
.attr("x", function(d) { return x(Math.min(0, d.value1)); })
.attr("y", function(d) { return y(d.name); })
.attr("width", function(d) { return Math.abs(x(d.value1) - x(0)); })
.attr("height", y.rangeBand());


But for some reason its not working . What is the mistake I am doing here. Thanks for the help in Advance.

Answer

If you are always guaranteed to have 2 bars per group, a quick fix would be:

combos
      .append("rect")
      .attr("class", function(d) { return "ba ba--" + (d.value < 0 ? "negative" : "positive"); })
      .attr("x", function(d) { return x(Math.min(0, d.value)); })
      .attr("y", function(d) { return y(d.name); })
      .attr("width", function(d) { return Math.abs(x(d.value) - x(0)); })
      .attr("height", y.rangeBand() / 2); //<-- cut height in half

  combos
      .append("rect")
      .attr("class", function(d) { return "bar bar--" + (d.value < 0 ? "negative" : "positive"); })
      .attr("x", function(d) { return x(Math.min(0, d.value1)); })
      .attr("y", function(d) { return y(d.name) + (y.rangeBand() / 2); }) //<-- offset if from other bar
      .attr("width", function(d) { return Math.abs(x(d.value1) - x(0)); })
      .attr("height", y.rangeBand() / 2); //<-- cut height in 2

Updated fiddle.

EDITS FOR COMMENTS

If you want to have a dynamic number of bars, you really need to change your input data. value1:, value2:, etc is just not workable. A quick refactor might use an array of values:

{
  "name": "M1",
  "value": [-45, -13, -10, -8]
},
...

With this format you can use a sub-selection and really start getting some dynamic code:

var combos = svg.selectAll("g").data(data)
  .enter()
  .append("g");    
combos
  .selectAll("rect")
  .data(function(d){
  return d.value.map(function(d1){
    return {
        value: d1,
        name: d.name,
        total: d.value.length,
      };
    })
  })
  .enter()
  .append("rect")
  .attr("class", function(d) { return "ba bar--" + (d.value < 0 ? "negative" : "positive"); })
  .attr("x", function(d) { return x(Math.min(0, d.value)); })
  .attr("y", function(d,i) { return (y(d.name) + (y.rangeBand() / d.total * i)) ; })
  .attr("width", function(d) { return Math.abs(x(d.value) - x(0)); })
  .attr("height", function(d){
    return y.rangeBand() / d.total
   });

New updated fiddle.