[Chartjs]-Adding Minor Gridlines in Logarithmic Chart in Charts.Js v2.9.4

1👍

As was the case in the OP linked post (which was designed for the latest chart.js v4), it is still valid for chart.js v2+ that minor grid lines are created if ticks.callback returns an empty string instead of null or undefined (or if there’s no ticks.callback).

However, there are differences in the way one may style the minor grid lines to differentiate them from major ones. In chart.js v2.9.4 scales’ gridLines options are not scriptable. This calls for a different approach, one that might in some cases be useful for newer versions: using indexable options,
which means that one has to create an array with a (different) value for each item – in this case for each grid line. Since the ticks are only known at runtime, one can create that array in an axis callback, e.g., in afterBuildTicks:

afterBuildTicks(scale){
    const nTicks = scale.ticks.length,
        isMajor = scale.ticks.map(value => ('' + value).replace(/0+$/, '') === '1');
    scale.options.gridLines.lineWidth = 
        Array.from({length: nTicks}, (_, i) => isMajor[i] ? 1.2 : 0.5);
    scale.options.gridLines.color = 
        Array.from({length: nTicks}, (_, i) => isMajor[i] ? '#000' : '#ddd');
    //scale.options.gridLines.borderDash - can't be set as an array of arrays
}

Unfortunately, this approach doesn’t seem to work for borderDash.


I’ll give some details about what exactly how this function works: using an indexable option, let’s say gridLines.lineWidth, means we set its value as an array:

gridLines:{
    lineWidth: [1.2, 0.5, 0.5, 0.5, 0.5, 1.2, 0.5, .......]
}

The problem is that the ticks are built by chart.js from the data, we don’t know beforehand how many ticks there are in total and how many and where are the major ones. So we have to wait for chart.js to build those ticks and then, in the handler automatically called immediately after the ticks are built, afterBuildTicks, we set the grid line properties, based on the actual tick values. This makes sense, since the gridlines are drawn after the ticks were built.

The function afterBuildTicks receives as an argument the scale object, from which we extract the ticks as scale.ticks. Then we decide which ticks will be "major" – to be drawn with a thicker line – the definition I used is that the string representation is a 1 fallowed by 0s. An alternative, mathematical formulation can be devised, using Math.log10.

Now, we know how many ticks there are and where are the major ones so we can build the array introduced above for gridLines.lineWidth. The function Array.from is used; alternatively, we could have used isMajor.map with the same functions.


Code snippet:

Chart.defaults.global.animation.duration = 1000;
Chart.defaults.global.animation.easing = 'linear';
const ctx = document.getElementById('myChart').getContext('2d');
new Chart(ctx, {
    type: 'line',
    data: {
        labels: ['10', '2000', '30000', '50000', '60000', '700000',],
        datasets: [{
            label: 'ABC',
            fill: true,
            data: [{
                x: '10',
                y: 2
                },
                {
                    x: '2000',
                    y: 4
                },
                {
                    x: '30000',
                    y: 7
                },
                {
                    x: '50000',
                    y: 8
                },
                {
                    x: '60000',
                    y: 8.5
                },
                {
                    x: '700000',
                    y: 9
                }
            ],
            borderWidth: 1
        },]
    },
    options: {
        aspectRatio: 1.6,
        title: {
            display: true,
            position: 'top',
            text: 'Ref Data'
        },
        legend: {
            display: true,
            position: 'right',
        },
        scales: {
            yAxes: [{
                id: 'first-y-Axis',
                display: true,
                scaleLabel: {
                    display: true,
                    labelString: 'Demo y-axis'
                }
            }],
            xAxes: [{
                id: 'first-x-Axis',
                display: true,
                type: 'logarithmic',
                scaleLabel: {
                    display: true,
                    labelString: 'Demo x-axis'
                },
                gridLines: {
                    borderDash: [10, 3],
                    drawTicks: false,
                    drawBorder: false
                },
                afterBuildTicks(scale){
                    const nTicks = scale.ticks.length,
                        isMajor = scale.ticks.map(value => ('' + value).charAt(0) === '1');
                    scale.options.gridLines.lineWidth = Array.from({length: nTicks}, (_, i) => isMajor[i] ? 1.2 : 0.5);
                    scale.options.gridLines.color = Array.from({length: nTicks}, (_, i) => isMajor[i] ? '#000' : '#ddd');
                    //scale.options.gridLines.borderDash - can't be set as an array of arrays
                },
                ticks: {
                    padding: 5,
                    autoSkip: false,
                    maxRotation: 0,
                    callback: function(value, index, values){
                        if(Math.log10(value) % 1 === 0){
                            return value;
                        }
                        return '';
                    }
                }
            }]
        },
        plugins: {
            colorschemes: {
                scheme: 'brewer.Paired12'
            }
        }
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.bundle.min.js"
        integrity="sha512-SuxO9djzjML6b9w9/I07IWnLnQhgyYVSpHZx0JV97kGBfTIsUYlWflyuW4ypnvhBrslz1yJ3R+S14fdCWmSmSA=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-colorschemes"></script>        
<div style="min-height: 60vh">
    <canvas id="myChart">
    </canvas>
</div>

Per request, a version that should work in Internet Explorer and older browsers:

afterBuildTicks: function(scale){
    var nTicks = scale.ticks.length,
        isMajor = scale.ticks.map(function(value){return ('' + value).replace(/0+$/, '') === '1';});
    scale.options.gridLines.lineWidth =
        isMajor.map(function(_, i){return isMajor[i] ? 1.2 : 0.5;})
    scale.options.gridLines.color =
        isMajor.map(function(_, i){return isMajor[i] ? '#000' : '#ddd';});
    //scale.options.gridLines.borderDash - can't be set as an array of arrays
}

and the full code in jsFiddle

Leave a comment