0

I have a scenario where we can have up to 6 different pie charts in a single "chart". What we want is to allow the user to click on a legend item and have it affect all pie charts with that item. Some charts may only have one slice with any value but all share a same base category set. Using the work done in this answer I was able to do this for two pie charts. But with any more than that I am getting only one pie with the slice hiding/showing.

This is the portion of the code that does the legendGroup (or legendItem - I have tried both):

var newChart = new Highcharts.Chart(options, function(chart) {
  $(chart.series[0].data).each(function(i, e) {
    console.log(chart.series[0].data);
    e.legendGroup.on('click', function(event) {
      var legendItem = e.name;
      console.log(legendItem);
      event.stopPropagation();

      $(chart.series).each(function(j, f) {
        $(this.data).each(function(k, z) {
          if (z.name == legendItem) {
            if (z.visible) {
              z.setVisible(false);
            } else {
              z.setVisible(true);
            }
          }
        });
      });
    });
  });
});

As you can see the console.log(legendItem); does not appear. It looks like the updated click event is not being captured.

How can I make this single legend click hide/show the slice in all pie charts with that item? Bonus for how to allow the hover effect in the pie charts when you mouse over the legend item as well.

Full jsFiddle code:

var options = {
  chart: {
    type: 'pie',
    renderTo: 'container',
    width: null,
    height: 800,
    events: {
      load: function() {
        var chart = this;
        var currWidth = chart.chartWidth;
        var currHeight = chart.chartHeight;
        var seriesList = chart.series;
        var seriesCount = seriesList.length;
        var piesize = CalcSize(currWidth, currHeight, seriesCount);
        var numCols;
        var numRows;
        var locArray = [];

        if (seriesCount == 6) {
          numCols = 2;
          numRows = 3;
          for (nR = 0; nR < numRows; nR++) {
            for (nC = 0; nC < numCols; nC++) {
              if (locArray.length <= seriesCount - 1) {
                locArray.push([((piesize / 2) + (piesize * nC)) + 60, (piesize / 2) + (piesize * nR)])
              }
            }
          }
        } else {
          numCols = currWidth / piesize;
          numRows = Math.floor(currHeight / piesize);
          for (nR = 0; nR < numRows; nR++) {
            for (nC = 0; nC < numCols; nC++) {
              if (locArray.length <= seriesCount - 1) {
                locArray.push([(piesize / 2) + (piesize * nC), ((piesize / 2) + (piesize * nR)) - 75])
              }
            }
          }
        }

        locArray.sort();

        $(seriesList).each(function(j) {
          seriesList[j].update({
            size: piesize,
            center: locArray[j]
          })
        });

        //console.log(currWidth);
        //console.log(currHeight);
        //console.log(seriesCount);
        //console.log(piesize);
        //console.log(numCols);
        //console.log(numRows);
        //console.log(locArray);
        //console.log(seriesList);
      },
      resize: function() {
        var chart = this;
        $(seriesList).each(function(j) {
          seriesList[j].update({
            center: [chart.series[0].data[1].plotX - 20, chart.series[0].data[1].plotY - 65]
          })
        });
      }
    }
  },
  title: {
    text: null
  },
  xAxis: {
    categories: ['Not Specified', 'Entry Level', 'Less than 1 year', '1 Year to 2 Years', '2 Years to 5 Years', '5 Years to 10 Years', 'More than 10 Years'],
    labels: {
      style: {}
    }
  },
  series: [{
    name: 'Chattanooga, TN-GA MSA',
    visible: true,
    showInLegend: true,
    dataLabels: {
      enabled: false
    },
    tooltip: {
      valueDecimals: null,
      valuePrefix: null,
      valueSuffix: null
    },
    data: [
      ['Not Specified', 12],
      ['Entry Level', 1],
      ['Less than 1 year', 0],
      ['1 Year to 2 Years', 0],
      ['2 Years to 5 Years', 4],
      ['5 Years to 10 Years', 1],
      ['More than 10 Years', 0]
    ]
  }, {
    name: 'Jackson, TN MSA',
    visible: true,
    showInLegend: false,
    dataLabels: {
      enabled: false
    },
    tooltip: {
      valueDecimals: null,
      valuePrefix: null,
      valueSuffix: null
    },
    data: [
      ['Not Specified', 1],
      ['Entry Level', 0],
      ['Less than 1 year', 0],
      ['1 Year to 2 Years', 0],
      ['2 Years to 5 Years', 2],
      ['5 Years to 10 Years', 0],
      ['More than 10 Years', 0]
    ]
  }, {
    name: 'Kingsport-Bristol-Bristol, TN-VA MSA',
    visible: true,
    showInLegend: false,
    dataLabels: {
      enabled: false
    },
    tooltip: {
      valueDecimals: null,
      valuePrefix: null,
      valueSuffix: null
    },
    data: [
      ['Not Specified', 0],
      ['Entry Level', 0],
      ['Less than 1 year', 0],
      ['1 Year to 2 Years', 1],
      ['2 Years to 5 Years', 2],
      ['5 Years to 10 Years', 0],
      ['More than 10 Years', 0]
    ]
  }, {
    name: 'Knoxville, TN MSA',
    visible: true,
    showInLegend: false,
    dataLabels: {
      enabled: false
    },
    tooltip: {
      valueDecimals: null,
      valuePrefix: null,
      valueSuffix: null
    },
    data: [
      ['Not Specified', 27],
      ['Entry Level', 2],
      ['Less than 1 year', 0],
      ['1 Year to 2 Years', 1],
      ['2 Years to 5 Years', 4],
      ['5 Years to 10 Years', 0],
      ['More than 10 Years', 0]
    ]
  }, {
    name: 'Memphis, TN-MS-AR MSA',
    visible: true,
    showInLegend: false,
    dataLabels: {
      enabled: false
    },
    tooltip: {
      valueDecimals: null,
      valuePrefix: null,
      valueSuffix: null
    },
    data: [
      ['Not Specified', 45],
      ['Entry Level', 18],
      ['Less than 1 year', 0],
      ['1 Year to 2 Years', 14],
      ['2 Years to 5 Years', 31],
      ['5 Years to 10 Years', 2],
      ['More than 10 Years', 0]
    ]
  }, {
    name: 'Nashville-Davidson--Murfreesboro, TN MSA',
    visible: true,
    showInLegend: false,
    dataLabels: {
      enabled: false
    },
    tooltip: {
      valueDecimals: null,
      valuePrefix: null,
      valueSuffix: null
    },
    data: [
      ['Not Specified', 176],
      ['Entry Level', 10],
      ['Less than 1 year', 0],
      ['1 Year to 2 Years', 16],
      ['2 Years to 5 Years', 31],
      ['5 Years to 10 Years', 3],
      ['More than 10 Years', 0]
    ]
  }],
  tooltip: {
    useHTML: false,
    hideDelay: 75,
    valuePrefix: null,
    valueSuffix: null
  },
  subtitle: {
    text: null
  },
  plotOptions: {
    pie: {
      allowPointSelect: true,
      borderWidth: 0,
      dataLabels: {
        enabled: false
      }
    }
  }
};

function CalcSize(width, height, numberCharts) {
  var number = numberCharts; // Example-Number
  var width = width;
  var height = height;
  var area = height * width;
  var elementArea = parseInt(area / number);

  // Calculate side length if there is no "spill":
  var sideLength = parseInt(Math.sqrt(elementArea));

  // We now need to fit the squares. Let's reduce the square size 
  // so an integer number fits the width.
  var numX = Math.ceil(width / sideLength);
  sideLength = width / numX;
  while (numX <= number) {
    // With a bit of luck, we are done.
    if (Math.floor(height / sideLength) * numX >= number) {
      // They all fit! We are done!
      return sideLength;
    }
    // They don't fit. Make room for one more square i each row.
    numX++;
    sideLength = width / numX;
  }
  // Still doesn't fit? The window must be very wide
  // and low.
  sideLength = height;
  return sideLength;
}

var newChart = new Highcharts.Chart(options, function(chart) {
  $(chart.series[0].data).each(function(i, e) {
    console.log(chart.series[0].data);
    e.legendGroup.on('click', function(event) {
      var legendItem = e.name;
      console.log(legendItem);
      event.stopPropagation();

      $(chart.series).each(function(j, f) {
        $(this.data).each(function(k, z) {
          if (z.name == legendItem) {
            if (z.visible) {
              z.setVisible(false);
            } else {
              z.setVisible(true);
            }
          }
        });
      });
    });
  });
});
wergeld
  • 14,332
  • 8
  • 51
  • 81

1 Answers1

1

For some reason (bug probably) legendItemClick doesn't fire for the pie series (that would be appropriate place to handle this custom behavior).

As a workaround I overwrote the core function Highcharts.Legend.prototype.setItemEvents and implemented the desired functionality there: https://jsfiddle.net/kkulig/236gpt4s/

    // custom code - handle categories        
    item.series.chart.series.forEach(function(s) {
        s.points.forEach(function(p) {
        if(p.name === item.name) {
            p.setVisible();
        }
      });
    });
Kamil Kulig
  • 5,756
  • 1
  • 8
  • 12
  • If I could upvote more I would! Added similar code as your little snippet there to the `'mouseover'` and `'mouseout'` events to set the slices for each pie to be selected when legend item is hovered over. Thank you very much. – wergeld Jan 12 '18 at 12:26