Chartjs-How can I create a stacked bar chart with Charts.JS that displays relative rather than absolute values?

3👍

Your idea is actually quite good, but as you declared in your question, it won’t be dynamic and then won’t work with every chart of this type.

A way to make it dynamic is to use Chart.js plugins where the beforeInit event will be handled to edit the data to fit your percentage problem, and where the afterDraw event will be handled to write the percentage over the bar :

Chart.pluginService.register({

    // This event helps to edit the data in our datasets to fit a percentage scale
    beforeInit: function(chartInstance) {

        // We first get the total of datasets for every data
        var totals = [];
        chartInstance.data.datasets.forEach(function(dataset) {
            for (var i = 0; i < dataset.data.length; i++) {
                var total = 0;
                chartInstance.data.datasets.forEach(function(dataset) {
                    total += dataset.data[i];
                });
                totals.push(total);
            }
        });

        // And use it to calculate the percentage of the current data
        chartInstance.data.datasets.forEach(function(dataset) {
            for (var i = 0; i < dataset.data.length; i++) {
                // This ----------¬     is useful to add it as a string in the dataset
                // It solves      V      the problem of values with `.0` at decimals
                dataset.data[i] = '' + (dataset.data[i] / totals[i]) * 100;
            }
        });
    },

    // This event allows us to add the percentage on the bar
    afterDraw: function(chartInstance) {

        // We get the canvas context
        var ctx = chartInstance.chart.ctx;

        // And set the properties we need
        ctx.font = Chart.helpers.fontString(14, 'bold', Chart.defaults.global.defaultFontFamily);
        ctx.textAlign = 'center';
        ctx.textBaseline = 'bottom';
        ctx.fillStyle = '#666';

        // For every dataset ...
        chartInstance.data.datasets.forEach(function(dataset) {

            // If it is not the first dataset, we return now (no action)
            if (dataset._meta[0].controller.index != 0) return;

            // For ervery data in the dataset ...
            for (var i = 0; i < dataset.data.length; i++) {

                // We get the model of the data
                var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;

                // And use it to display the text where we want
                ctx.fillText(parseFloat(dataset.data[i]).toFixed(2) + "%", ((model.base + model.x) / 2), (model.y + model.height / 3));
            }
        });
    }
});

You can see this code working on this jsFiddle and here is its result :

enter image description here

Leave a comment