Stefan
Stefan

Reputation: 12370

How to filter stacked bar chart?

I found following example for a stacked bar chart with dc.js: https://dc-js.github.io/dc.js/examples/stacked-bar.html

enter image description here If I click on some (or several) legend item, I would like the chart to

I already managed to add some click event to the legend entries:

chart.on('pretransition.hideshow', ()=> {
    chart.selectAll('.dc-legend-item')    
      .on('click', function (data, index, nodeList) {
        const stackName = data.name;
        if(data.hidden){
           chart.showStack(stackName);
        } else {
           chart.hideStack(stackName);
        }  
        dc.redrawAll();
      });
  });

This hides some stack but the sum is not shown as expected (multiplie, overlapping values are shown).

enter image description here

=>How can I filter the data correctly?

I also tried to use chart.filter() but that only seems to be able filter the x axis and not the stacks.

Currently, if I hover over a legend entry, the chart already adapts but does not show the wanted behavior.

Upvotes: 1

Views: 276

Answers (1)

Stefan
Stefan

Reputation: 12370

Thanks to Gordon I found following solution:

Step 1: Create an extra dimension for the stack property:

const stackDimension = crossFilter.dimension(d => d.stackProperty);

Step 2: Create an event handler and filter on that dimension:

  const selectedStackNames = [];

  const legendItemClickHandler = (data, index, nodeList) => {
   
    const stackName = data.name;       
    if(selectedStackNames.includes(stackName)){
       const index = selectedStackNames.indexOf(stackName);
       selectedStackNames.splice(index,1);          
    } else {
       selectedStackNames.push(stackName);          
    }

    if(selectedStackNames.length){
      stackDimension.filter((name)=>{
          return selectedStackNames.includes(name);
      });
    } else {
      stackDimension.filter(null);
    }  
    dc.redrawAll();
    
  };

  chart.on('pretransition.hideshow', ()=> {
    chart.selectAll('.dc-legend-item')
      .on('click', legendItemClickHandler);
  });

Step 3: Highlight selected legend items

 chart.on('pretransition.show', ()=> {
    chart.selectAll('.dc-legend-item')
      .on('click', legendItemClickHandler);

    const selectedStackNames = new Set(
      stackDimension.top(Infinity)
        .map(d=>d.stackProperty)
    );
    
    chart.selectAll('.dc-legend-item')
      .each((data, index, nodeList)=>{
        const node = nodeList[index];
        const colorRect = node.children[0];
        if(selectedStackNames.has(data.name)){
          colorRect.style.outline = "1px solid grey";
          colorRect.opacity="";
          data.hidden=false;
        } else {
          colorRect.style.outline = "";
          data.hidden=true;
          colorRect.opacity="0.3";
        }
      });
  });

Upvotes: 1

Related Questions