HTML
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<title>Chart.js HTML Legend</title>
<meta name="keywords" content="LightYear,光年,后台模板,后台管理系统,光年HTML模板">
<meta name="description" content="LightYear是一个基于Bootstrap的后台管理系统的HTML模板。">
<meta name="author" content="yinqi">
<link rel="stylesheet" type="text/css" href="http://lyear.itshubao.com/v4/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="http://lyear.itshubao.com/v4/css/style.min.css">
</head>
<body>
<div class="chart-example">
<div class="tab-content">
<div class="chart-view">
<div id="legend-container"></div>
<canvas id="myChart"></canvas>
</div>
<div class="chart-actions mt-3"></div>
<ul class="nav nav-tabs mt-3">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#myChart-plugin" aria-selected="true">Plugin</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#myChart-data" aria-selected="false">Data</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#myChart-config" aria-selected="false">Config</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active tab-pane-code" id="myChart-plugin">
<pre>
const getOrCreateLegendList = (chart, id) => {
const legendContainer = document.getElementById(id);
let listContainer = legendContainer.querySelector('ul');
if (!listContainer) {
listContainer = document.createElement('ul');
listContainer.style.display = 'flex';
listContainer.style.flexDirection = 'row';
listContainer.style.margin = 0;
listContainer.style.padding = 0;
legendContainer.appendChild(listContainer);
}
return listContainer;
};
const htmlLegendPlugin = {
id: 'htmlLegend',
afterUpdate(chart, args, options) {
const ul = getOrCreateLegendList(chart, options.containerID);
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = chart.options.plugins.legend.labels.generateLabels(chart);
items.forEach(item => {
const li = document.createElement('li');
li.style.alignItems = 'center';
li.style.cursor = 'pointer';
li.style.display = 'flex';
li.style.flexDirection = 'row';
li.style.marginLeft = '10px';
li.onclick = () => {
const {type} = chart.config;
if (type === 'pie' || type === 'doughnut') {
// Pie and doughnut charts only have a single dataset and visibility is per item
chart.toggleDataVisibility(item.index);
} else {
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
}
chart.update();
};
// Color box
const boxSpan = document.createElement('span');
boxSpan.style.background = item.fillStyle;
boxSpan.style.borderColor = item.strokeStyle;
boxSpan.style.borderWidth = item.lineWidth + 'px';
boxSpan.style.display = 'inline-block';
boxSpan.style.height = '20px';
boxSpan.style.marginRight = '10px';
boxSpan.style.width = '20px';
// Text
const textContainer = document.createElement('p');
textContainer.style.color = item.fontColor;
textContainer.style.margin = 0;
textContainer.style.padding = 0;
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
const text = document.createTextNode(item.text);
textContainer.appendChild(text);
li.appendChild(boxSpan);
li.appendChild(textContainer);
ul.appendChild(li);
});
}
};
</pre>
</div>
<div class="tab-pane fade tab-pane-code" id="myChart-data">
<pre>
const NUM_DATA = 7;
const NUM_CFG = {count: NUM_DATA, min: 0, max: 100};
const data = {
labels: Utils.months({count: NUM_DATA}),
datasets: [
{
label: 'Dataset: 1',
data: Utils.numbers(NUM_CFG),
borderColor: Utils.CHART_COLORS.red,
backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5),
fill: false,
},
{
label: 'Dataset: 1',
data: Utils.numbers(NUM_CFG),
borderColor: Utils.CHART_COLORS.blue,
backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5),
fill: false,
},
],
};
</pre>
</div>
<div class="tab-pane fade tab-pane-code" id="myChart-config">
<pre>
const config = {
type: 'line',
data: data,
options: {
plugins: {
htmlLegend: {
// ID of the container to put the legend in
containerID: 'legend-container',
},
legend: {
display: false,
}
}
},
plugins: [htmlLegendPlugin],
};
</pre>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="http://lyear.itshubao.com/v4/js/jquery.min.js"></script>
<script type="text/javascript" src="http://lyear.itshubao.com/v4/js/popper.min.js"></script>
<script type="text/javascript" src="http://lyear.itshubao.com/v4/js/bootstrap.min.js"></script>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/Chart.js/4.1.1/chart.umd.min.js"></script>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/luxon/3.2.0/luxon.min.js"></script>
<script type="text/javascript" src="http://libs.itshubao.com/chartjs/utils.js"></script>
</body>
</html>
JavaScript
const getOrCreateLegendList = (chart, id) => {
const legendContainer = document.getElementById(id);
let listContainer = legendContainer.querySelector('ul');
if (!listContainer) {
listContainer = document.createElement('ul');
listContainer.style.display = 'flex';
listContainer.style.flexDirection = 'row';
listContainer.style.margin = 0;
listContainer.style.padding = 0;
legendContainer.appendChild(listContainer);
}
return listContainer;
};
const htmlLegendPlugin = {
id: 'htmlLegend',
afterUpdate(chart, args, options) {
const ul = getOrCreateLegendList(chart, options.containerID);
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = chart.options.plugins.legend.labels.generateLabels(chart);
items.forEach(item => {
const li = document.createElement('li');
li.style.alignItems = 'center';
li.style.cursor = 'pointer';
li.style.display = 'flex';
li.style.flexDirection = 'row';
li.style.marginLeft = '10px';
li.onclick = () => {
const {type} = chart.config;
if (type === 'pie' || type === 'doughnut') {
// Pie and doughnut charts only have a single dataset and visibility is per item
chart.toggleDataVisibility(item.index);
} else {
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
}
chart.update();
};
// Color box
const boxSpan = document.createElement('span');
boxSpan.style.background = item.fillStyle;
boxSpan.style.borderColor = item.strokeStyle;
boxSpan.style.borderWidth = item.lineWidth + 'px';
boxSpan.style.display = 'inline-block';
boxSpan.style.height = '20px';
boxSpan.style.marginRight = '10px';
boxSpan.style.width = '20px';
// Text
const textContainer = document.createElement('p');
textContainer.style.color = item.fontColor;
textContainer.style.margin = 0;
textContainer.style.padding = 0;
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
const text = document.createTextNode(item.text);
textContainer.appendChild(text);
li.appendChild(boxSpan);
li.appendChild(textContainer);
ul.appendChild(li);
});
}
};
const NUM_DATA = 7;
const NUM_CFG = {count: NUM_DATA, min: 0, max: 100};
const data = {
labels: Utils.months({count: NUM_DATA}),
datasets: [
{
label: 'Dataset: 1',
data: Utils.numbers(NUM_CFG),
borderColor: Utils.CHART_COLORS.red,
backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5),
fill: false,
},
{
label: 'Dataset: 1',
data: Utils.numbers(NUM_CFG),
borderColor: Utils.CHART_COLORS.blue,
backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5),
fill: false,
},
],
};
const config = {
type: 'line',
data: data,
options: {
plugins: {
htmlLegend: {
// ID of the container to put the legend in
containerID: 'legend-container',
},
legend: {
display: false,
}
}
},
plugins: [htmlLegendPlugin],
};
const ctx = document.getElementById('myChart');
const chart = new Chart(ctx, config);