Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
384 views
in Technique[技术] by (71.8m points)

javascript - How can I force my ChartJS canvas legend to stay in a single column?

I've checked some different sources and looked around the options, but I can't seem to be able to get my legend to stay in one column. For example,

JS Chart

In the picture above, you'll notice one piece of the legend is getting cut off and placed to the side. This happens at around <= 550 pixels. I would like to force them all to remain in one column. Here is a JSFiddle with the chart recreated. I had to paste in some imports in the beginning of the JS file, because I couldn't find them in the fiddle options. Scroll to the bottom for the relevant stuff. Any help would be appreciated. https://jsfiddle.net/lochrine/02yrpcxg/

Here is the relevant JS:

//Line Graph Script

$('.line-graph').each(function () {
    var legendlabels = $(this).data('legendlabels');
    var datapoints = $(this).data('datapoints');
    var suppliers = $(this).data('suppliers');

    var datatype = $(this).data('datatype');
    var yAxisString = "Amounts";
    if (datatype == "units") { yAxisString = "Units Sold"; }
    else if (datatype == "money") { yAxisString = "Amount (Dollars)"; }

    console.log(datatype);

    new Chart($(this).get(0).getContext('2d'), {
        type: 'line',
        data: {
            labels: legendlabels,
            datasets: $.map(datapoints, function (e, i) {
                return {
                    backgroundColor: lineChartColors[i],
                    borderColor: lineChartColors[i],
                    fill: false,
                    data: e,
                    label: suppliers[i],
                    lineTension: 0.2,
                }
            })
        },
        options: {
            layout: {
                padding: {
                    left: 20,
                    right: 40,
                    top: 20,
                    bottom: 20
                }
            },
            legend: {
                display: true,
                position: 'left'

            },
            scales: {
                xAxes: [{
                    scaleLabel: {
                        display: true,
                        labelString: 'Month'
                    }
                }],
                yAxes: [{
                    ticks: {
                        beginAtZero: true,
                        callback: function (value, index, values) {
                            return addCommas(value);
                        }
                    },
                    scaleLabel: {
                        display: true,
                        labelString: yAxisString 
                    }
                }]
            },
            plugins: {
                datalabels: {
                    display: false
                }
            },
            tooltips: {
            callbacks: {
                label: function (tooltipItem, data) {
                    var datasetLabel = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].toString();
                    var label = data.datasets[tooltipItem.datasetIndex].label + ': ';
                    var formattedReturnLabel;
                    if (datatype == "money") {
                        formattedReturnLabel = label + '$' + addCommas(datasetLabel);
                    } else {
                        formattedReturnLabel = label + addCommas(datasetLabel);
                    }
                    return formattedReturnLabel;
                }
            }
        }
        }
    });

})

And the relevant HTML:

<div class="widget widget-double">
    <div class="m-3 border">
        <table style="cursor: pointer !important;" onclick="window.location.href='@Url.Action("SupplierUnitsByMonth", "Reports")'" class="table mb-0"><thead><tr><th class="text-center">@ViewBag.widgetName</th></tr></thead></table>
        <div class="w-100 aspect-ratio-50 p-2">
            <canvas id="chart-units-history" data-legendlabels="[@ViewBag.Months]" data-suppliers= "[@ViewBag.suppliers]" data-datapoints="[@ViewBag.supplierTotals]" data-datatype="units" class="line-graph w-100 aspect-ratio-50"></canvas>
        </div>
    </div>
</div>
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can generate custom HTML legend using legendCallback together with some CSS.

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 + '')">' +
      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 your amended code and see how it works.

const lineChartColors = ["#000000", "#fd7730", "#ffd35c", "#3fc6f3", "#28a745", "#488cf2", "#4755d3", "#9768c9", "#f2748d", "#f287e7", '#992499', '#6BD69E'];
const legendlabels = ['Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'];
const datapoints = [
  [205, 275, 359, 329, 262, 302, 290, 323, 279, 238, 307, 245],
  [16, 13, 14, 11, 23, 11, 24, 23, 15, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 0],
  [169, 194, 261, 193, 151, 158, 128, 143, 163, 173, 139, 208],
  [8, 5, 8, 2, 4, 4, 0, 0, 0, 0, 0, 0],
  [0, 0, 19, 36, 7, 35, 27, 30, 13, 0, 0, 0],
  [0, 47, 30, 54, 59, 48, 41, 38, 65, 24, 44, 37],
  [12, 16, 27, 33, 18, 46, 70, 89, 23, 41, 71, 0]
];
var suppliers = ["Total", "Starkey", "Resound", "Widex", "Rexton", "Unitron", "Phonak", "Signia"];

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('myChart', {
  type: 'line',
  data: {
    labels: legendlabels,
    datasets: datapoints.map((e, i) => ({
      backgroundColor: lineChartColors[i],
      borderColor: lineChartColors[i],
      fill: false,
      data: e,
      label: suppliers[i],
      lineTension: 0.2,
    }))
  },
  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 + '')">' +
          ds.label + '</span>' +
          '</li>';
      });
      return html + '</ul>';
    },
    scales: {
      xAxes: [{
        scaleLabel: {
          display: true,
          labelString: 'Month'
        }
      }],
      yAxes: [{
        ticks: {
          beginAtZero: true
        },
        scaleLabel: {
          display: true,
          labelString: "Units Sold"
        }
      }]
    },
    plugins: {
      datalabels: {
        display: false
      }
    }
  }
});
document.getElementById("legend").innerHTML = chart.generateLegend();
#chart-wrapper {
  display: flex;
}

ul {
  list-style-type: none;
  margin: 0;
  padding: 0;
}

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

#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 id="chart-wrapper">
  <div id="legend"></div>
  <canvas id="myChart" height="75"></canvas>
</div>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...