score:0

Here's a chart which resets the domain with new options based on the toggled legend:

JS Fiddle DEMO

function toggleBar(name, state) {
    data.forEach(function(d) { 
       _.findWhere(d.valores, {name: name}).hidden = state;
    });
    var filteredOptions;
    if(state) {
        filteredOptions = options.filter(function(d) { return d !== name; });
    } else {
      filteredOptions = options;
    }

    x1.domain(filteredOptions).rangeRoundBands([0, x0.rangeBand()]);

    y.domain([0, d3.max(data, function(d) {
      return d3.max(d.valores.filter(function(k) { return !k.hidden;}), function(d) {
        return d.value;
      });
    })]);

Changes:

  1. You don't need to reset the data on every toggle. I just added a hidden attribute to the "valores" and the while resetting the domain in the toggleBar function, filtered the data based on non-hidden options and set the domain accordingly.

  2. I'd recommend to get used to d3's "enter, update and exit" methods. I hope the code helps you understand that as well. drawBars() is a function that does that.

  3. Changed the way the tooltip is rendered as well. Instead of using querySelector for hovered elements (that's definitely one way), you can just use the parent node's data using the datum() function.

  4. Legends: I've added a stroke for every legend and to indicate whether the corresponding option is hidden or not, the fill-opacity is toggled on every click.

  5. Used a separate color scale with an ordinal domain of options and range to be same as previous colors so that the colors are based on the names and not indices (as before)

  6. Added simple transitions.

  7. Used underscore.js in toggleBars() function. You could switch back to pure JS as well.

And to answer your question on active states, please check for toggling of the "clicked" classnames.

Please go through the code and let me know if you any part of it is unclear. I'll add some comments too.

:)

score:0

here is a solution for grouped bar chart legend toggling with animation.

//jsfiddle - http://jsfiddle.net/0ht35rpb/259/

var $this =  this.$('.barChart');

var w = $this.data("width");
var h = $this.data("height");

//var configurations = $this.data("configurations");

var data = [{
  "State": "a",
  "AA": 100,
  "BB": 200
}, {
  "State": "b",
  "AA": 454,
  "BB": 344
},{
  "State": "c",
  "AA": 140,
  "BB": 500
}, {
  "State": "d",
  "AA": 154,
  "BB": 654
}];

var yLabel = "Count";


var svg = d3.select($this[0]).append("svg"),
  margin = {
    top: 20,
    right: 20,
    bottom: 30,
    left: 40
  },
  width = w - margin.left - margin.right,
  height = h - margin.top - margin.bottom,
  g = svg
  .attr("width", w)
  .attr("height", h)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// The scale spacing the groups:
var x0 = d3.scaleBand()
  .rangeRound([0, width])
  .paddingInner(0.1);

// The scale for spacing each group's bar:
var x1 = d3.scaleBand()
  .padding(0.05);

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

var z = d3.scaleOrdinal()
  .range(["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f", "#8bf41b"]);

var keys = d3.keys(data[0]).slice(1);

x0.domain(data.map(function(d) {
  return d.State;
}));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) {
  return d3.max(keys, function(key) {
    return d[key];
  });
})]).nice();

g.append("g")
  .selectAll("g")
  .data(data)
  .enter().append("g")
  .attr("class", "bar")
  .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("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, i) {            
    return z(d.key);
  });

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

g.append("g")
  .attr("class", "yaxis")
  .call(d3.axisLeft(y).ticks(null, "s"))
  .append("text")
  .attr("x", 2)
  .attr("y", y(y.ticks().pop()) + 0.5)
  .attr("dy", "0.32em")
  .attr("fill", "#000")
  .attr("font-weight", "bold")
  .attr("text-anchor", "start")
  .text(yLabel);

var legend = g.append("g")
  .attr("font-family", "sans-serif")
  .attr("font-size", 10)
  .attr("text-anchor", "end")
  .selectAll("g")
  .data(keys.slice().reverse())
  .enter().append("g")
  .attr("transform", function(d, i) {
    return "translate(0," + i * 20 + ")";
  });

legend.append("rect")
  .attr("x", width - 17)
  .attr("width", 15)
  .attr("height", 15)
  .attr("fill", z)
  .attr("stroke", z)
  .attr("stroke-width", 2)
  .on("click", function(d) {
    update(d)
  });

legend.append("text")
  .attr("x", width - 24)
  .attr("y", 9.5)
  .attr("dy", "0.32em")
  .text(function(d) {
    return d;
  });

var filtered = [];

////
//// Update and transition on click:
////

function update(d) {

  //
  // Update the array to filter the chart by:
  //

  // add the clicked key if not included:
  if (filtered.indexOf(d) == -1) {
    filtered.push(d);
    // if all bars are un-checked, reset:
    if (filtered.length == keys.length) filtered = [];
  }
  // otherwise remove it:
  else {
    filtered.splice(filtered.indexOf(d), 1);
  }

  //
  // Update the scales for each group(/states)'s items:
  //
  var newKeys = [];
  keys.forEach(function(d) {
    if (filtered.indexOf(d) == -1) {
      newKeys.push(d);
    }
  })
  x1.domain(newKeys).rangeRound([0, x0.bandwidth()]);
  y.domain([0, d3.max(data, function(d) {
    return d3.max(keys, function(key) {
      if (filtered.indexOf(key) == -1) return d[key];
    });
  })]).nice();

//g.select(".yaxis")
//.call(d3.axisLeft(y).ticks(null, "s"));

var t0 = svg.transition().duration(250);
var t1 = t0.transition();
t1.selectAll(".yaxis").call(d3.axisLeft(y).ticks(null, "s"));


  //
  // Filter out the bands that need to be hidden:
  //
  var bars = svg.selectAll(".bar").selectAll("rect")
    .data(function(d) {
      return keys.map(function(key) {
        return {
          key: key,
          value: d[key]
        };
      });
    })

  bars.filter(function(d) {
      return filtered.indexOf(d.key) > -1;
    })
    .transition()
    .attr("x", function(d) {
      return (+d3.select(this).attr("x")) + (+d3.select(this).attr("width")) / 2;
    })
    .attr("height", 0)
    .attr("width", 0)
    .attr("y", function(d) {
      return height;
    })
    .duration(500);

  //
  // Adjust the remaining bars:
  //
  bars.filter(function(d) {
      return filtered.indexOf(d.key) == -1;
    })
    .transition()
    .attr("x", function(d) {
      return x1(d.key);
    })
    .attr("y", function(d) {
      return y(d.value);
    })
    .attr("height", function(d) {
      return height - y(d.value);
    })
    .attr("width", x1.bandwidth())
    .attr("fill", function(d, i) {
      return z(d.key);
    })
    .duration(500);

  // update legend:
  legend.selectAll("rect")
    .transition()
    .attr("fill", function(d, i) {
      if (filtered.length) {
        if (filtered.indexOf(d) == -1) {
          return z(d);
        } else {
          return "white";
        }
      } else {
        return z(d);
      }
    })
    .duration(100);
}

Related Query

More Query from same tag