Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wrap legend text in Chartjs?

My ChartJs legend text is overflowing in the same line when the text is too long. Is there any parameter that I can use to enable text-wrap.

legend : {
    display : true,
    position : 'bottom',
    fullWidth: false,
    labels : {
        fontColor : "#000",
        //  boxWidth  : "3"
    }
}

In other chart libraries like highcharts, you just have to set width and the text will be wrapped if it exceeds the width. Is there such an option in ChartJS?

Highcharts Library Example:

legend: {
    itemStyle: {
        width: 90 // or whatever, auto-wrap
    },
}

I tried using legendCallback, but in that case I will loose the 'onclick' useful functionalities of the legend boxes that come out of the box in ChartJS. So I don't want to do that. Don't want use legend template either.

like image 283
Dhanya S V Avatar asked Nov 24 '16 17:11

Dhanya S V


1 Answers

Unfortunately there is no option for automatically wrap long legend labels.

You'll have to generate custom HTML legend using legendCallback together with some CSS. The following code expects that dataset.label to be either a string or an array of strings in case of a multi-line label (For some types of charts (i.e. pie chart), individual legend labels represent a value from a unique dataset. In such cases, the code looks slightly different as shown in this answer).

legendCallback: chart => {
  let html = '<ul>';
  chart.data.datasets.forEach((ds, i) => {
    html += '<li>' +
      '<span style="width: 36px; height: 14px; background-color:' + ds.backgroundColor + '; border:' + ds.borderWidth + 'px solid ' + ds.borderColor + '" onclick="onLegendClicked(event, \'' + i + '\')">&nbsp;</span>' +
      '<span id="legend-label-' + i + '" onclick="onLegendClicked(event, \'' + i + '\')">' +
      (Array.isArray(ds.label) ? ds.label.join('<br/>') : ds.label) + '</span>' +
      '</li>';
  });
  return html + '</ul>';
},

To make this behave the same as standard Chart.js charts, the function onLegendClicked is invoked when a mouse click occurs on a legend label. This function toggles the hidden state of individual datasets and changes label text style between normal and strike-through.

function onLegendClicked(e, i) {
  const hidden = !chart.data.datasets[i].hidden;
  chart.data.datasets[i].hidden = hidden;
  const legendLabelSpan = document.getElementById("legend-label-" + i);
  legendLabelSpan.style.textDecoration = hidden ? 'line-through' : '';
  chart.update();
};

Please take a look at the executable code below and see how it works in action:

function onLegendClicked(e, i) {
  const hidden = !chart.data.datasets[i].hidden;
  chart.data.datasets[i].hidden = hidden;
  const legendLabelSpan = document.getElementById("legend-label-" + i);
  legendLabelSpan.style.textDecoration = hidden ? 'line-through' : '';
  chart.update();
};

const chart = new Chart(document.getElementById("chart"), {
  type: "bar",
  data: {
    labels: ['A', 'B', 'C'],
    datasets: [{
        label: ['Legend label', 'on two lines'],
        data: [5, 8, 4],
        fill: false,
        backgroundColor: "rgba(255, 99, 132, 0.2)",
        borderColor: "rgb(255, 99, 132)",
        borderWidth: 1
      },
      {
        label: ['Legend label', 'spread over', 'three lines'],
        data: [3, 5, 4],
        fill: false,
        backgroundColor: "rgba(255, 159, 64, 0.2)",
        borderColor: "rgb(255, 159, 64)",
        borderWidth: 1
      },
      {
        label: "Short legend label",
        data: [6, 5, 7],
        fill: false,
        backgroundColor: "rgba(255, 205, 86, 0.2)",
        borderColor: "rgb(255, 205, 86)",
        borderWidth: 1
      }
    ]
  },
  options: {
    legend: {
      display: false
    },
    legendCallback: chart => {
      let html = '<ul>';
      chart.data.datasets.forEach((ds, i) => {
        html += '<li>' +
          '<span style="width: 36px; height: 14px; background-color:' + ds.backgroundColor + '; border:' + ds.borderWidth + 'px solid ' + ds.borderColor + '" onclick="onLegendClicked(event, \'' + i + '\')">&nbsp;</span>' +
          '<span id="legend-label-' + i + '" onclick="onLegendClicked(event, \'' + i + '\')">' +
          (Array.isArray(ds.label) ? ds.label.join('<br/>') : ds.label) + '</span>' +
          '</li>';
      });
      return html + '</ul>';
    },
    scales: {
      yAxes: [{
        ticks: {
          beginAtZero: true
        }
      }]
    }
  }
});
document.getElementById("legend").innerHTML = chart.generateLegend();
#legend>ul {
  display: flex;
  justify-content: center;
}

#legend li {
  cursor: pointer;
  margin: 0px 10px;
  display: flex;
}

#legend li span {
  padding-left: 8px;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<div>  
  <canvas id="chart" height="60"></canvas>
  <div id="legend"></div>
</div>
like image 50
uminder Avatar answered Sep 19 '22 14:09

uminder