22

In Chart.js, is there any way to write the labels inside of the horizontal bars in a "horizontalBar" chart? As in something like:

this graph

Is anything similar to this possible in Chart.js?

Thanks!

antonin_scalia
  • 1,073
  • 2
  • 10
  • 11

6 Answers6

31

There is now on option "mirror" to make the label appear inside the bar.

Example of "options" config for a horizontalBar chart :

options: {
    scales: {
        yAxes: [{ticks: {mirror: true}}]
    }
}

Doc : http://www.chartjs.org/docs/latest/axes/cartesian/#common-configuration

Sibiraj
  • 4,486
  • 7
  • 33
  • 57
Elie
  • 504
  • 5
  • 5
  • 2
    In the ticks documentation it says that its only for vertical scales. Are you sure it works on horizontal? – crthompson Mar 10 '17 at 05:14
  • I can't show you the dashboard that I built, but yes, it also works for horizontal bar charts. – Elie Mar 11 '17 at 13:37
  • 4
    It's only working for yAxis, so you can only display your labels inside the bars if they are aligned horizontal. – bonbon Jul 11 '17 at 16:09
9

With reference to the original solution to displaying data value by using HTML5 Canvas fillText() method in the animation's onComplete, the code below will get you what you need:

The key to custom-displaying any chart related data is inspecting the chart's dataset as in this.data.datasets below. I did this by inspecting where the different values are in this dataset, as well as the position to place them for the fillText method.

Inspecting the chart's dataset: Image

Script (Chart.js 2.0 & up):

var barOptions = {
      events: false,
        showTooltips: false,
      legend: {
        display: false
      },
      scales:{
        yAxes: [{
          display: false
        }]
      },
        animation: {
        onComplete: function () {
          var ctx = this.chart.ctx;
          ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily);
          ctx.textAlign = 'left';
          ctx.textBaseline = 'bottom';

          this.data.datasets.forEach(function (dataset) {
            for (var i = 0; i < dataset.data.length; i++) {
              var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model,
                  left = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._xScale.left;
              ctx.fillStyle = '#444'; // label color
              var label = model.label;
              ctx.fillText(label, left + 15, model.y + 8);
            }
          });               
        }
        }
    };

jsFiddle: http://jsfiddle.net/jfvy12h9/

Community
  • 1
  • 1
Hung Tran
  • 1,570
  • 1
  • 13
  • 13
  • a quick test shows it works. but, labels disappear when hovering and showing the toopltip. any workaround for this? – benzkji Apr 25 '19 at 18:06
  • adding to `hover: { animationDuration: 1 }` disables animation of the tooltips, but solves the flickering/on/off problem... – benzkji Apr 25 '19 at 18:20
3

You can achieve this effect in chartjs 2.0 + by rotating the labels 90 degrees and applying negative padding, so that the label moves up inside the 'bar'. Something like this:

new Chart(document.getElementById(id), {
  type: 'bar',
  data: data,
  options: {
    scales: {
      xAxes: [
        {
          ticks: {
            autoSkip: false,
            maxRotation: 90,
            minRotation: 90,
            padding: -110
          }
        }
      ]
    }
  }
});

See the tick configuration documentation for more information.

omnikron
  • 2,211
  • 17
  • 30
  • Thanks. For horizontal (x axis) ticks negative padding "padding: -30" is sufficient to move them up inside the chart area. No rotation necessary (chart.js v2.9.4). – Patronaut Nov 06 '20 at 13:33
0

It seems like there is no direct option for this right now. A possible implementation is iterating over bars/columns by using onAnimationComplete hook. You can view this question for detailed explanations how to display data values on Chart.js

I would like you remind that this has a drawback. Since it uses onAnimationComplete, whenever an animation happens on chart, values will be disappear for a second and appear again.

Community
  • 1
  • 1
0

Since on "bar" chart doesn't work the "mirror" option...

I've found a workaround solution: Fiddle link

I used a chart.js 2.8.0 version modified in this link

<html>
    <body>
        <canvas id="myChart" width="800" height="600">
        </canvas>
    </body>
</html>





var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, {
type: "bar",
data: {
labels: ["ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT"],
datasets: [{
    label: "Quota Eni mensile",
    backgroundColor: [
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)"
    ],
    borderColor: [
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)",
        "rgba(255, 255, 153, 1)"
    ],
    borderWidth: 1,
    data: [10, 11, 18, 10, 13, 28, 12, 16]
  }
  ]
},
options: {
    legend: {
        display: false
    },
    scales: {
        xAxes: [{
            ticks:{
                maxRotation: 90,
                minRotation: 90,
                display: "top"
             },          
         }],
         yAxes: [{
             display: false
         }]
     },
     tooltips: {
         enabled: true
     },
     maintainAspectRatio: true,
     responsive: true
 }

});

Chart.plugins.register({
/* Adjust axis labelling font size according to chart size */
  id: "responsiveXlabels",
  beforeDraw: function(c) {
      var chartHeight = c.chart.height;
      var size = (chartHeight * 2.5) / 100;
      var xAxis = c.scales["x-axis-0"];
      xAxis.options.ticks.minor.fontSize = size;
      xAxis.height = 10;
      xAxis.minSize.height = 10;
      c.options.scales.xAxes[0].ticks.padding = -size * 4.5;
      xAxis.margins.bottom = size * 12.5;
  },
  afterDraw: function(c) {
      c.options.scales.xAxes[0].ticks.padding = -158;
  }

});

the responsiveXlabel plugin is used to transform text on resize. There's only one thing remained to solve: align text at the bottom of the axis.

Hope this helps who wants to implement mirroring on X Axis and satisfy responsive request.

0

In recent versions of chart.js (v3+) the best way to achieve this seems to be through the datalabels plugin

https://github.com/chartjs/chartjs-plugin-datalabels

plugins: [ChartDataLabels], // Register the plugin
options: {
    indexAxis: 'y', // Assume this is for a horizontal bar chart
    scales: {
        y: {
            display: false // Hide the standard labels
        }
    },
    plugins: {
        datalabels: {
            anchor: 'start', // Anchor the labels to the start of the datapoint
            align: 'end', // Align the text after the anchor point
            formatter: function(value, context) { // Show the label instead of the value
                return context.chart.data.labels[context.dataIndex];
            }
        }
    }
}

If you're using a stacked bar, you'll then just need to hide these labels for all but the first dataset by setting data.datasets[].datalabels.labels.display to false.

Phil
  • 951
  • 12
  • 16