Search code examples
javascriptjquerychartschart.jsbar-chart

How to line break a label object in ChartsJS (nested arrays won't work)


As answered here I'm using nested arrays to line break my labels on ChartsJS and it works fine but if I try to use nested arrays on my label (the object inside dataset) they won't line break.

For example in this graph:

enter image description here

I have 2 labels with new lines but if I try the same technique with my label (the legend above the graph) it won't line break.

Here is the code for this graph:

data: {  labels: [ 'Utensilios para escrita e artes','Faz de conta','Jogos',['Materiais não estruturado','/de largo alcançe/recicláveis'],['Repertório artístico-cultural e','científico de diferentes','origens étnico-raciais'],'Livros de história','Materiais para pesquisa',],

            datasets: [
                    { maxBarThickness: 70,label: 'Não há material presente',
                    backgroundColor: '#eb7071',

                    borderWidth: 1,
                    data: [ 2,41,24,51,78,33,62,]
                    }, 
                    { maxBarThickness: 70,label: ['Material presente,',' mas crianças não usaram'],
                    backgroundColor: '#f9ae60',

                    borderWidth: 1,
                    data: [ 24,38,48,39,15,48,30,]
                    }, 
                    { maxBarThickness: 70,label: 'Crianças usaram os materiais',
                    backgroundColor: '#73e599',

                    borderWidth: 1,
                    data: [ 73,21,28,10,7,20,7,]
                    }, 

                ]
            },

I assumed the legend would not line break because in the code it was not a nested array so I changed my label to this:

label: [['Material presente,',' mas crianças não usaram']]

But it still did not work.

Is there any way to line break these legends without having to generate them as a HTML script to manipulate them?


Solution

  • There's no option that can be defined to easily achieve what you're looking for. An open feature request exists for such an option since 2016.

    You'll have to generate custom HTML legend using legendCallback.

    Inspired by this answer, I came up with the following code that illustrates how this can be done in your case. It shouldn't be too hard to further improve the styling of the legend labels and adapted them to your needs.

    const chart = new Chart(document.getElementById("chart"), {
      type: "bar",
      data: {
        labels: [
          ['Utensilios para', 'escrita e artes'],
          ['Materiais não estruturado', 'de largo alcançe/recicláveis'],
          ['Repertório artístico-cultural e', 'científico de diferentes', 'origens étnico-raciais']
        ],
        datasets: [{
            label: "Não há material presente",
            data: [5, 8, 4],
            fill: false,
            backgroundColor: "rgba(255, 99, 132, 0.2)",
            borderColor: "rgb(255, 99, 132)",
            borderWidth: 1
          },
          {
            label: ["Material presente", "mas crianças não usaram"],
            data: [3, 5, 4],
            fill: false,
            backgroundColor: "rgba(255, 159, 64, 0.2)",
            borderColor: "rgb(255, 159, 64)",
            borderWidth: 1
          },
          {
            label: "Crianças usaram os materiais",
            data: [6, 5, 7],
            fill: false,
            backgroundColor: "rgba(255, 205, 86, 0.2)",
            borderColor: "rgb(255, 205, 86)",
            borderWidth: 1
          }
        ]
      },
      options: {
        legend: {
          display: false
        },
        tooltips: {
          callbacks: {        
            title: (tooltipItems, data) => data.labels[tooltipItems[0].index],
            label: (tooltipItems, data) =>
              data.datasets[tooltipItems.datasetIndex].label + ': ' + tooltipItems.value + '%'
          }
        },
        legendCallback: chart => {
          let html = '<ul class="' + chart.id + '-legend">';
          chart.data.datasets.forEach((ds, i) => {
            html += '<li><span id="legend-' + i + '-item" style="background-color:' + ds.backgroundColor + '" onclick="updateDataset(event, \'' + i + '\')">';
            html += Array.isArray(ds.label) ? ds.label.join('<br />') : ds.label;
            html += '</span></li>';
          });
          return html + '</ul>';
        },
        scales: {
          yAxes: [{
            ticks: {
              beginAtZero: true
            }
          }]
        }
      }
    });
    
    $("#legend").html(chart.generateLegend());
    
    // Show/hide chart by click legend
    updateDataset = (e, dsIndex) => {
      let hide = !chart.data.datasets[dsIndex].hidden;
      chart.data.datasets[dsIndex].hidden = hide;
      if (hide) {
        $('#' + e.path[0].id).css("text-decoration", "line-through");
      } else {
        $('#' + e.path[0].id).css("text-decoration", "");
      }
      chart.update();
    };
    #legend>ul {
      padding: 0;
      text-align: center;
    }
    
    #legend li {
      cursor: pointer;
      margin: 10px 10px;
      display: inline-table;
    }
    
    #legend li span {
      position: relative;
      padding: 3px 5px;
      z-index: 2;
      font-family: Arial, Helvetica, sans-serif;
      font-size: 12px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
    <div>
      <div id="legend"></div>
      <canvas id="chart"></canvas>
    </div>

    UPDATE

    Meanwhile I answered a similar question and invested some time to properly define the styling of the legend labels. Please have a look at this anwser.