Search code examples
javascripttooltipz-indexchart.js

Charts.js tooltip overlapping text on chart


As Charts.js does not yet support annotations, I have added annotations of the data points after the chart is drawn. using ctx.fillText as shown below.

        animation: {
            animateScale: true,
            animateRotate: true,
            onComplete: function () {
                var chartInstance = this.chart,
                    ctx = chartInstance.ctx;
                ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
                ctx.textAlign = 'center';
                ctx.fillStyle = this.chart.config.options.defaultFontColor;
                ctx.textBaseline = 'bottom';

                this.data.datasets.forEach(function (dataset, i) {
                    var meta = chartInstance.controller.getDatasetMeta(i);
                    meta.data.forEach(function (bar, index) {
                        data = dataset.data[index];
                        ctx.fillText(data, bar._model.x, bar._model.y - 5);
                    });
                });
            }
        }

This works great, other than the fact that now the tooltip is shown below the newly added text. This is not that obvious, however sometimes it overlaps in a bad place meaning that you cannot see the tooltip behind.

enter image description here

Is there a way to set the z-index of the ctx.fillText or tooltip so I can layer them correctly?


Solution

  • For anyone interested I managed to figure this out, I ended up looking at the tooltip drawing functions within charts.js and using a modified version of this as a custom tooltip, thus drawing the tooltip after the annotations are added.

    First add this to your opptions

    config = {
        options: {
            tooltips: {
                enabled: false,
                custom: customTooltips
            }
    

    This then calls the custom tooltip function below.

    var currentX = null;
    var currentY = null;
    
    var customTooltips = function (tooltip) {
    
        var helpers = Chart.helpers;
        var ctx = this._chart.ctx;
        var vm = this._view;
    
        if (vm == null || ctx == null || helpers == null || vm.opacity === 0) {
            return;
        }
    
        var tooltipSize = this.getTooltipSize(vm);
    
        var pt = {
            x: vm.x,
            y: vm.y
        };
    
        if (currentX == vm.x && currentY == vm.y) {
            return;
        }
    
        currentX = vm.x;
        currentY = vm.y;
    
       //  IE11/Edge does not like very small opacities, so snap to 0
        var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
    
        // Draw Background
        var bgColor = helpers.color(vm.backgroundColor);
        ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString();
        helpers.drawRoundedRectangle(ctx, pt.x, pt.y, tooltipSize.width, tooltipSize.height, vm.cornerRadius);
        ctx.fill();
    
        // Draw Caret
        this.drawCaret(pt, tooltipSize, opacity);
    
        // Draw Title, Body, and Footer
        pt.x += vm.xPadding;
        pt.y += vm.yPadding;
    
        // Titles
        this.drawTitle(pt, vm, ctx, opacity);
    
        // Body
        this.drawBody(pt, vm, ctx, opacity);
    
        // Footer
        this.drawFooter(pt, vm, ctx, opacity);
    };