Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Showing Percentage and Total In stacked Bar Chart of chart.js

I am working on stacked Bar charts using chart.js. I need to show labels in middle of Bars as percentage and total sum on top of bars stacked together. Currently, I am able to show their percentage after searching for code. But that percentages are not correct mathematically. I have added that code in js fiddle. Hope I got some help for it. I am just weak in js.
https://jsfiddle.net/n4nish/hca3wdgq/4/

HTML -

  var data = [{
     label: 'New',
     backgroundColor: '#1d3f74',
     data: [6310, 5742, 4044, 5564]
        }, {
     label: 'Repeat',
     backgroundColor: '#6c92c8',
     data: [11542, 12400, 12510, 11450]
        }];


 var options = {
     maintainAspectRatio: false,
     spanGaps: false,
     responsive: true,
     legend: {
         display: true,
         position: 'bottom',
         labels: {
             fontColor: "#fff",
             boxWidth: 14,
             fontFamily: 'proximanova'
         }
     },
     tooltips: {
         mode: 'label',
         callbacks: {
             label: function (tooltipItem, data) {
                 var type = data.datasets[tooltipItem.datasetIndex].label;
                 var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
                 var total = 0;
                 for (var i = 0; i < data.datasets.length; i++)
                     total += data.datasets[i].data[tooltipItem.index];
                 if (tooltipItem.datasetIndex !== data.datasets.length - 1) {
                     return type + " : " + value.toFixed(0).replace(/(\d)(?=(\d{3})+\.)/g, '1,');
                 } else {
                     return [type + " : " + value.toFixed(0).replace(/(\d)(?=(\d{3})+\.)/g, '1,'), "Overall : " + total];
                 }
             }
         }
     },
     plugins: {
         datalabels: {
             formatter: function (value, ctx) {
                 let sum = 0;
                 let dataArr = ctx.chart.data.datasets[0].data;
                 dataArr.map(data => {
                     sum += data;
                 });
                 let percentage = (value * 100 / sum).toFixed(0) + "%";
                 return percentage;
             },
             font: {
                 weight: "normal"
             },
             color: "#fff"
         }
     },
     scales: {
         xAxes: [{
             stacked: true,
             gridLines: {
                 display: false
             },
             ticks: {
                 fontColor: "#fff"
             }
                }],
         yAxes: [{
             stacked: true,
             display: false,
             ticks: {
                 fontColor: "#fff"
             }
                }]
     }

 };

 var ctx = document.getElementById("mychart").getContext('2d');

 var myChart = new Chart(ctx, {
     type: 'bar',
     data: {
         labels: ["Jun", "July", "Aug", "Sept"],
         datasets: data
     },
     options: options
 });
like image 722
Nishant Sharma Avatar asked Mar 05 '23 17:03

Nishant Sharma


1 Answers

You can set options that will apply to:

all labels in the chart: options.plugins.datalabels.*

only a single dataset: dataset.datalabels.*

// Label formatter function
const formatter = (value, ctx) => {
  const otherDatasetIndex = ctx.datasetIndex === 0 ? 1 : 0;
  const total =
    ctx.chart.data.datasets[otherDatasetIndex].data[ctx.dataIndex] + value;

  return `${(value / total * 100).toFixed(0)}%`;
};

const data = [{
    // stack: 'test',
    label: "New",
    backgroundColor: "#1d3f74",
    data: [6310, 5742, 4044, 5564],
    // Change options only for labels of THIS DATASET
    datalabels: {
      color: "white",
      formatter: formatter
    }
  },
  {
    // stack: 'test',
    label: "Repeat",
    backgroundColor: "#6c92c8",
    data: [11542, 12400, 12510, 11450],
    // Change options only for labels of THIS DATASET
    datalabels: {
      color: "yellow",
      formatter: formatter
    }
  }
];

const options = {
  maintainAspectRatio: false,
  spanGaps: false,
  responsive: true,
  legend: {
    display: true,
    position: "bottom",
    labels: {
      fontColor: "#fff",
      boxWidth: 14,
      fontFamily: "proximanova"
    }
  },
  tooltips: {
    mode: "label",
    callbacks: {
      label: function(tooltipItem, data) {
        const type = data.datasets[tooltipItem.datasetIndex].label;
        const value =
          data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
        let total = 0;
        for (let i = 0; i < data.datasets.length; i++)
          total += data.datasets[i].data[tooltipItem.index];
        if (tooltipItem.datasetIndex !== data.datasets.length - 1) {
          return (
            type + " : " + value.toFixed(0).replace(/(\d)(?=(\d{3})+\.)/g, "1,")
          );
        } else {
          return [
            type +
            " : " +
            value.toFixed(0).replace(/(\d)(?=(\d{3})+\.)/g, "1,"),
            "Overall : " + total
          ];
        }
      }
    }
  },
  plugins: {
    // Change options for ALL labels of THIS CHART
    datalabels: {
      color: "#white",
      align: "center"
    }
  },
  scales: {

    xAxes: [{
        stacked: true,
        gridLines: {
          display: false
        },
        ticks: {
          fontColor: "#fff"
        }
      },
      {
        type: 'category',
        offset: true,
        position: 'top',
        ticks: {
          fontColor: "#fff",
          callback: function(value, index, values) {
            return data[0].data[index] + data[1].data[index]
          }
        }
      }
    ],
    yAxes: [{
      stacked: true,
      display: false,
      ticks: {
        fontColor: "#fff"
      }
    }]
  }
};

const ctx = document.getElementById("mychart").getContext("2d");

new Chart(ctx, {
  type: "bar",
  data: {
    labels: ["Jun", "July", "Aug", "Sept"],
    datasets: data
  },
  options: options
});
body {
  background: #20262e;
  font-family: Helvetica;
  padding-top: 50px;
}

#mychart {
  height: 300px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chartjs-plugin-datalabels.min.js"></script>

<canvas id="mychart"></canvas>

Codepen

like image 168
ksav Avatar answered Mar 07 '23 07:03

ksav