Search code examples
chartsgoogle-visualizationbar-chartlegendstacked-bar-chart

Custom Legend multiple colors in Google Chart


I am facing a problem that I cannot custom the default legend color of this chart to multiple colors, please help me. This is image describe my problem , I am using stacked bar chart. This is my code:

//dump data arr
var data = google.visualization.arrayToDataTable([[["","0 times (never)",{"role":"style"},"1 times",{"role":"style"},"2 times",{"role":"style"},"3 times or more",{"role":"style"}],["A class",0.581,"#b4ddfd",0.109,"#84bfef",0.21,"#559ad2",0.1,"#4277a1"],["nationality",0.481,"#ffddba",0.209,"#ffc384",0.25,"#ffac5b",0.06,"#fa993f"]],[["","0 times (never)",{"role":"style"},"1 times",{"role":"style"},"2 times",{"role":"style"},"3 times or more",{"role":"style"}],["A class",0.1,"#b4ddfd",0.2,"#84bfef",0.3,"#559ad2",0.4,"#4277a1"],["nationality",0.4,"#ffddba",0.3,"#ffc384",0.2,"#ffac5b",0.1,"#fa993f"]],[["","0 times (never)",{"role":"style"},"1 times",{"role":"style"},"2 times",{"role":"style"},"3 times or more",{"role":"style"}],["A class",0.5,"#b4ddfd",0.5,"#84bfef",0,"#559ad2",0,"#4277a1"],["nationality",0,"#ffddba",0,"#ffc384",0,"#ffac5b",1,"#fa993f"]],[["","0 times (never)",{"role":"style"},"1 times",{"role":"style"},"2 times",{"role":"style"},"3 times or more",{"role":"style"}],["A class",0.01,"#b4ddfd",0.02,"#84bfef",0.03,"#559ad2",0.94,"#4277a1"],["nationality",0.4,"#ffddba",0.3,"#ffc384",0.2,"#ffac5b",0.1,"#fa993f"]]]);
var options = {
        series: {
            0: {
                color: '#b4ddfd'
            },
            1: {
                color: '#84bfef'
            },
            2: {
                color: '#559ad2'
            },
            3: {
                color: '#4277a1'
            },
        },
        vAxis: {
            textStyle: {fontSize: 11},
            titleTextStyle: {italic: false},
        },
        chartArea: {
            width: '85%',
            height: areaHeight,
            top: 30,
            left: '13%'
        },
        bar: {groupWidth: '35%'},
        legend: {
            position: 'bottom',
            textStyle: {fontSize: 11},
        },
        isStacked: 'percent',
        hAxis: {
            ticks: [0, 1],
            textStyle: {fontSize: 13},
            minValue: 0,
            maxValue: 1,
        },
        callbackLegend: function(legend) {
            // my problem here
            // var legend_div = document.getElementById(graphId + '_legend');
            // legend_div.innerHTML = legend.innerHTML;
        },
        width: 920,
        height: areaHeight + 100
    };
var chart = new google.visualization.BarChart(document.getElementById('#chart_div'));
    chart.draw(data, options);

Please help me, I am deadlocking.


Solution

  • the standard legend on a google chart will not display multiple colors.
    in fact, when using the style column role,
    the legend will not match the colors used in the style columns in the data table.

    instead, we can build a custom legend to display the multiple colors for each series.

    to build the legend, we need to add a container to hold the legend entries.
    we can use a <div> element placed just below the chart.

    <div id="chart_div"></div>
    <div id="legend_div"></div>
    

    we can use css to style the legend container and use the same width of the chart.

    #legend_div {
      font-family: Arial;
      font-size: 11px;
      text-align: center;
      width: 920px;
    }
    

    in the following example, I extract the colors for each series from the data table.
    using the values provided in the style column roles.
    to create the legend content, I use the following html templates.

    one for the legend entry itself...

    <script id="template-legend-entry" type="text/html">
      <div class="legend-entry" data-columnIndex="{{index}}">
        {{colors}}
        <span>{{label}}</span>
      </div>
    </script>
    

    and another for each color to be displayed for that series...

    <script id="template-legend-entry-color" type="text/html">
      <div class="legend-entry-color" style="background-color: {{color}}"></div>
    </script>
    

    in this example, only two rows exist in the data table,
    so two colors will be displayed for each legend entry.

    the legend is built during the chart's 'ready' event,
    so as soon as the chart is finished drawing,
    the legend will be displayed.

    during which, the colors are extracted from the data table and used to build the legend entries.

    a click event is added for example purposes, in case there are actions you would like to take when a legend entry is clicked.
    in this example, the chart series is selected to highlight which legend entry was clicked.

    google.charts.load('current', {
      packages: ['corechart']
    }).then(function () {
      var data = google.visualization.arrayToDataTable([
        ["","0 times (never)",{"role":"style"},"1 times",{"role":"style"},"2 times",{"role":"style"},"3 times or more",{"role":"style"}],
        ["A class",0.581,"#b4ddfd",0.109,"#84bfef",0.21,"#559ad2",0.1,"#4277a1"],
        ["nationality",0.481,"#ffddba",0.209,"#ffc384",0.25,"#ffac5b",0.06,"#fa993f"]
      ]);
    
      var options = {
        vAxis: {
          textStyle: {
            fontSize: 11
          },
          titleTextStyle: {
            italic: false
          }
        },
        chartArea: {
          width: '85%',
          top: 30,
          left: '13%'
        },
        bar: {
          groupWidth: '35%'
        },
        legend: {
          position: 'none'
        },
        isStacked: 'percent',
        hAxis: {
          ticks: [0, 1],
          textStyle: {
            fontSize: 13
          },
          minValue: 0,
          maxValue: 1
        },
        width: 920,
        height: '100%'
      };
    
      var chart = new google.visualization.BarChart(document.getElementById('chart_div'));
    
      // chart ready event
      google.visualization.events.addListener(chart, 'ready', function () {
        // legend container
        var legend = document.getElementById('legend_div');
        legend.innerHTML = '';
    
        // build legend from chart data
        var colorPallette = [];
        for (var colIndex = 0; colIndex < data.getNumberOfColumns(); colIndex++) {
          // determine if style column
          if (data.getColumnRole(colIndex) === 'style') {
            // save colors for entire series (all rows)
            var seriesColors = '';
            for (var rowIndex = 0; rowIndex < data.getNumberOfRows(); rowIndex++) {
              seriesColors += renderTemplate('template-legend-entry-color', {
                color: data.getValue(rowIndex, colIndex)
              });
            }
    
            // add legend for series (all colors)
            legend.insertAdjacentHTML('beforeEnd', renderTemplate('template-legend-entry', {
              colors: seriesColors,
              index: colIndex - 1,
              label: data.getColumnLabel(colIndex - 1)
            }));
          }
        }
    
        // add click event to legend entries
        var legendEntries = legend.getElementsByClassName('legend-entry');
        Array.prototype.forEach.call(legendEntries, function(entry) {
          entry.addEventListener('click', function (e) {
            // find legend entry
            var entry = e.target || e.srcElement;
            if (entry.className.toLowerCase() !== 'legend-entry') {
              entry = entry.parentNode;
            }
    
            // get data table column index from legend entry
            var columnIndex = parseInt(entry.getAttribute('data-columnIndex'));
    
            // display legend entry that was clicked
            document.getElementById('message_div').innerHTML = 'legend entry clicked = ' + data.getColumnLabel(columnIndex);
    
            // select chart series
            chart.setSelection([{row: null, column: columnIndex}]);
          }, false);
        });
      });
    
      // render html template
      function renderTemplate(templateId, templateProps) {
        var content = document.getElementById(templateId).innerHTML;
        for (var handle in templateProps) {
          if (templateProps.hasOwnProperty(handle)) {
            content = content.replace('{{' + handle + '}}', templateProps[handle]);
          }
        }
        return content.trim();
      }
    
      // draw chart
      chart.draw(data, options);
    });
    #legend_div {
      font-family: Arial;
      font-size: 11px;
      text-align: center;
      width: 920px;
    }
    
    .legend-entry {
      display: inline-block;
      padding: 16px 4px 8px 4px;
    }
    
    .legend-entry-color {
      display: inline-block;
      height: 12px;
      width: 12px;
    }
    <script src="https://www.gstatic.com/charts/loader.js"></script>
    <div id="chart_div"></div>
    <div id="legend_div"></div>
    <div id="message_div"></div>
    
    <script id="template-legend-entry" type="text/html">
      <div class="legend-entry" data-columnIndex="{{index}}">
        {{colors}}
        <span>{{label}}</span>
      </div>
    </script>
    
    <script id="template-legend-entry-color" type="text/html">
      <div class="legend-entry-color" style="background-color: {{color}}"></div>
    </script>