javascripthtmlchart.js

How to maintain chart area in chart.js?


I'm making a website where people can browse data by region. Basically, it shows a bar chart, and there's a dropdown menu at the top that updates the data based on the selection.

One detail I'm having trouble with: I'd like the area of the chart to be identical regardless of the data shown. However, depending on the magnitude of the data, the area beside the y-axis will be wider or narrower. You can see what I mean in the simplified example below. The second image has more space to the left of the chart to accommodate the larger numbers, which slightly squishes the area of the actual chart. I would like the chart area to be identical between charts.

Chart with narrow border

Chart with wider border

I've looked into the "padding" option, but it just seems to add white space around the chart, which isn't quite what I want. Here's some sample code. Anyone have any ideas?

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
        <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    </head>

    <body>

        <select name="nation_select" id="nation_dropdown" onchange=update_plot()>
            <option value="nation_1">Nation 1</option>
            <option value="nation_2">Nation 2</option>
        </select>
        <div><canvas id="canvas_national_ts" width="1000" height="480"></canvas></div>

        <script>

            // Make plot
            function update_plot() {
                nation_selected = document.getElementById('nation_dropdown').value;

                // Set the data
                var time = [1,2,3,4,5];
                if (nation_selected == 'nation_1') {var data = [1,2,1,3,2];}
                if (nation_selected == 'nation_2') {var data = [1000000,1040000,970000,1200000,900000];}

                // Make a plot
                const ctx_national_ts = document.getElementById('canvas_national_ts');
                if (Chart.getChart(ctx_national_ts)) {
                    Chart.getChart(ctx_national_ts).destroy();
                };
                var chart_national_ts = new Chart(ctx_national_ts, {
                    type: 'bar',
                    data: {
                        labels: time,
                        datasets: [{
                            data: data,
                        }]
                    }
                })
            }

            // Initial plot
            update_plot();

        </script>
    </body>
</html>

Solution

  • You could use the the ticks.callback method in the axis configuration to fill the tick marks with leading spaces when the numbers are low. For example:

    options: {
      scales: {
        y: {
          ticks: {
            callback: function(value, index, ticks) {
                wdth = 12;
                lft = value == 0 ? wdth - 1 : wdth - Math.ceil(Math.log10(Math.abs(value)));
                spacer = '\u2007';
                return spacer.repeat(lft) + value;
            }
          }
        }
      }
    }
    
    

    In this example i use Math.ceil(Math.log10(Math.abs(value))) to compute the number of digits of the value. I specify the default width (in this example wdth = 12). Now I can add the required spaces (I use the figure space \u2007) in front of the value to get a fixed width.