Chart.js how to modify an existing legend - javascript

How do I modify an existing legend in Chart.js?
I've seen complex methods of creating a custom HTML legend (using generateLegend or legendCallback), and a simple method of options.legend.legendText which should accept an array, but saw no change so assumed that was for version 1.
I'm looking to add text to the default legend:
type: 'doughnut',
data: {
datasets: [{
data: series,
}],
labels: labels,
},
options: {
legend: {
legendText = labels.map((label, index) => `${label} - ${series[index]}%`);
}
}

Edit:
I noticed that if the chart was redrawn (e.g. if the browser window is resized) the legend would lose the extra text.
I've modified the approach to work as an inline plugin so that the label object is modified before the legend is drawn.
let labels = ['a', 'b', 'c', 'd'],
series = [4, 2, 1, 3],
myChart = new Chart(document.getElementById('chart'), {
type: 'doughnut',
data: {
labels: labels,
datasets: [{
data: series,
backgroundColor: ['red', 'blue', 'green', 'orange']
}]
},
options: {
maintainAspectRatio: false
},
plugins: [{
afterLayout: function(chart) {
let total = chart.data.datasets[0].data.reduce((a, b) => {
return a + b;
});
chart.legend.legendItems.forEach(
(label) => {
let value = chart.data.datasets[0].data[label.index];
label.text += ' - ' + (value / total * 100).toFixed(0) + '%'
return label;
}
)
}
}]
});
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<canvas id="chart"></canvas>

Chart.js 3.xx
I've included a sample for version 3.5 too.
You can alter the legend text by overriding the generateLabels method.
let labels = ['a', 'b', 'c', 'd'],
series = [4, 2, 1, 3],
myChart = new Chart(document.getElementById('chart'), {
type: 'doughnut',
data: {
labels: labels,
datasets: [{
data: series,
backgroundColor: ['red', 'blue', 'green', 'orange']
}]
},
options: {
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: "bottom",
align: "center",
fontFamily: "Arial",
labels: {
usePointStyle: true,
fontColor: "red",
generateLabels(chart) {
const data = chart.data;
if (data.labels.length && data.datasets.length) {
const {labels: {pointStyle}} = chart.legend.options;
return data.labels.map((label, i) => {
const meta = chart.getDatasetMeta(0);
const style = meta.controller.getStyle(i);
return {
text: 'This is ' + label + ' - ' + chart.data.datasets[0].data[i],
fillStyle: style.backgroundColor,
strokeStyle: style.borderColor,
lineWidth: style.borderWidth,
pointStyle: pointStyle,
hidden: !chart.getDataVisibility(i),
index: i
};
});
}
return [];
}
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.min.js"></script>
<canvas id="chart"></canvas>

Related

How can I add a euro sign (€) to all tooltips in my chart js line chart

I've got a line chart that has a tooltip on each data point. The data are prices so I want to add a euro sign before them but this seems harder than it sounds.
My code:
const labelsjaar = [
'jan',
'feb',
'mrt',
'apr',
'mei',
'jun',
'jul',
'aug',
'sept',
'okt',
'nov',
'dec',
];
const datajaar = {
labels: labelsjaar,
datasets: [{
label: 'Omzet',
backgroundColor: 'rgb(230 0 126)',
borderColor: 'rgb(230 0 126)',
data: [0,0,0,0,0,0,0,24,177,590.44,801.38,98.62],
}]
};
Chart.defaults.font.family = 'Panton';
Chart.defaults.font.size = 16;
const configjaar = {
type: 'line',
data: datajaar,
options: {
maintainAspectRatio: false,
tooltips: {
enabled: true,
mode: 'single',
callbacks: {
label: function (tooltipItems, data) {
var i = tooltipItems.index;
return data.labels[i] + ': ' + data.datasets[0].data[i] + ' €';
}
}
}
}
};
const myChartjaar = new Chart(
document.getElementById('myChartjaar'),
configjaar
);
I found this solution online:
tooltips: {
enabled: true,
mode: 'single',
callbacks: {
label: function (tooltipItems, data) {
var i = tooltipItems.index;
return data.labels[i] + ': ' + data.datasets[0].data[i] + ' €';
}
}
}
But my tooltips remain unchanged, there is no euro sign to be seen.
What am I doing wrong?
A jsfiddle of my chart can be seen here: https://jsfiddle.net/r4nw91bo/
After the comment below that I was looking at the wrong documentation I tried the following:
const labeltext = (tooltipItems) => {
tooltipItems.forEach(function(tooltipItem) {
tooltiplabel = '€' + tooltipItem.parsed.y.toLocaleString();
});
return tooltiplabel;
};
const configjaar = {
type: 'line',
data: datajaar,
options: {
plugins:{
tooltip:{
callbacks: {
label: labeltext,
}
}
},
maintainAspectRatio: false,
}
};
But this gives me the error: tooltipItems.forEach is not a function. If instead of label I use footer or title it works perfectly, but I don't want to add a title or a footer to my tooltip, I want to replace the existing content with my added € sign.
I also tried using their example for adding a dollar sign like this:
const configjaar = {
type: 'line',
data: datajaar,
options: {
plugins:{
tooltip:{
callbacks: {
label: function(context) {
const label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.parsed.y !== null) {
label += new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(context.parsed.y);
}
return label;
}
}
}
},
maintainAspectRatio: false,
}
};
But on hover of a data point this gives an error: Assignment to constant variable.
This is because you are using V2 syntax in V3, V3 has some major breaking changes over V2. Please read the migration guide for all of them.
For your callback to work you need to define it in options.plugins.tooltip.callbacks.label
EDIT:
Like the error says you are getting you can't reassign a constant variable since its a constant. If you change it to a let it works fine:
const options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'pink'
}]
},
options: {
plugins: {
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.parsed.y !== null) {
label += new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(context.parsed.y);
}
return label;
}
}
}
},
}
}
const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.0/chart.js"></script>
</body>

ChartJS show value in legend (Chart.js V3.5)

I need the value of chart show after name of data for example ([colour of data] Car 50, [colour of data] Motorcycle 200). I've tried change the value of legend title but it doesn't work at all
Here is it my code:
var ctx = document.getElementById('top-five').getContext('2d');
var myChartpie = new Chart(ctx, {
type: 'pie',
data: {
labels: {!! $top->pluck('name') !!},
datasets: [{
label: 'Statistics',
data: {!! $top->pluck('m_count') !!},
backgroundColor: {!! $top->pluck('colour') !!},
borderColor: {!! $top->pluck('colour') !!},
}]
},
options: {
plugins: {
legend: {
display: true,
title: {
text: function(context) {//I've tried to override this but doesn't work
var value = context.dataset.data[context.dataIndex];
var label = context.label[context.dataIndex];
return label + ' ' + value;
},
}
},
},
responsive: true,
}
});
You can use a custom generateLabels function for this:
var options = {
type: 'doughnut',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
}]
},
options: {
plugins: {
legend: {
labels: {
generateLabels: (chart) => {
const datasets = chart.data.datasets;
return datasets[0].data.map((data, i) => ({
text: `${chart.data.labels[i]} ${data}`,
fillStyle: datasets[0].backgroundColor[i],
index: i
}))
}
}
}
}
}
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.js"></script>
</body>
The below is a direct over ride of the default label generation found in the controller here. I have made one change on the text property within the generateLabels function in order to append the data value. It preserves the data toggling and strikethrough styling when a label is toggled.
plugins: {
legend: {
labels: {
generateLabels(chart) {
const data = chart.data;
if (data.labels.length && data.datasets.length) {
const {labels: {pointStyle}} = chart.legend.options;
return data.labels.map((label, i) => {
const meta = chart.getDatasetMeta(0);
const style = meta.controller.getStyle(i);
return {
text: `${label}: ${data['datasets'][0].data[i]}`,
fillStyle: style.backgroundColor,
strokeStyle: style.borderColor,
lineWidth: style.borderWidth,
pointStyle: pointStyle,
hidden: !chart.getDataVisibility(i),
// Extra data used for toggling the correct item
index: i
};
});
}
return [];
}
},
onClick(e, legendItem, legend) {
legend.chart.toggleDataVisibility(legendItem.index);
legend.chart.update();
}
}
//...
}
[1]: https://github.com/chartjs/Chart.js/blob/master/docs/samples/legend/html.md
You can also use the base implementation to reduce the amount of copied code. Note that some chart types (like donut) already overrides the default label generation.
plugins: {
legend: {
labels: {
generateLabels: function (chart) {
return Chart.defaults.plugins.legend.labels.generateLabels(chart).map(function (label) {
var dataset = chart.data.datasets[label.datasetIndex];
var total = 0;
for (var j = 0; j < dataset.data.length; j++)
total += dataset.data[j].y;
label.text = dataset.label + ': ' + total;
return label;
});
}
}
}
}

Get Dataset Values from Chart JS onHover in Version 3

I'm using Chart JS version 3.5.1, and I can't get the onHover function to work. My goal is to have the user hover or a data point, and then have the dataset values available to me inside the onHover.
Here's my setup:
var context = document.getElementById('canvas').getContext('2d')
var options = {
onHover: function(evt) {
console.log(evt)
var item = myChart.getElementAtEvent(evt)
console.log(item)
},
interaction: {
mode: 'index',
intersect: false,
},
plugins: {
legend: {
display: false,
}
},
responsive: true,
maintainAspectRatio: false
}
var myChart = new Chart(context, {
type: 'line',
data: {
labels: obj.labels,
datasets: [...]
},
options: options
})
I'm getting and error on this line:
var item = myChart.getElementAtEvent(evt)
The error says:
myChart.getElementAtEvent is not a function
I have confirmed that myChart is an object, but there doesn't appear to be a getElementAtEvent function.
I also tried the example in the docs here:
const canvasPosition = Chart.helpers.getRelativePosition(e, chart);
// Substitute the appropriate scale IDs
const dataX = chart.scales.x.getValueForPixel(canvasPosition.x);
const dataY = chart.scales.y.getValueForPixel(canvasPosition.y);
But it says:
Cannot read property 'getValueForPixel' of undefined
Does anyone know how I can successfully use onHover with version 3.5.1 of Chart JS?
You are using chart.js v3, in v3 there is no getElementAtEvent, it has been replaced with getElementsAtEventForMode. You can use this to get the active elements but you can also use the second argument the onHover function recieves which is an array with all the active elements:
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'pink'
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderColor: 'orange'
}
]
},
options: {
onHover: (evt, activeEls, chart) => {
if (activeEls.length === 0 || chart.getElementsAtEventForMode(evt, 'nearest', {
intersect: true
}, true).length === 0) {
return;
}
console.log('Function param: ', activeEls[0].index);
console.log('lookup with the event: ', chart.getElementsAtEventForMode(evt, 'nearest', {
intersect: true
}, true)[0].index);
activeEls.forEach(point => {
console.log('val: ', chart.data.datasets[point.datasetIndex].data[point.index])
})
}
}
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.js"></script>
</body>

Make Chart.js horizontal bar labels multi-line

Just wondering if there is any way to set the horizontal bar labels for y-axis using chart.js. Here is how I set up the chart:
<div class="box-body">
<canvas id="chart" style="position: relative; height: 300px;"></canvas>
</div>
Javascript:
var ctx = document.getElementById('chart').getContext("2d");
var options = {
layout: {
padding: {
top: 5,
}
},
responsive: true,
animation: {
animateScale: true,
animateRotate: true
},
};
var opt = {
type: "horizontalBar",
data: {
labels: label,
datasets: [{
data: price,
}]
},
options: options
};
if (chart) chart.destroy();
chart= new Chart(ctx, opt);
chart.update();
As you all can see, the first and third labels are too long and cut off. Is there a way to make the label multi-line?
If you want to have full control over how long labels are broken down across lines you can specify the breaking point by providing labels in a nested array. For example:
var chart = new Chart(ctx, {
...
data: {
labels: [["Label1 Line1:","Label1 Line2"],["Label2 Line1","Label2 Line2"]],
datasets: [{
...
});
You can use the following chart plugin :
plugins: [{
beforeInit: function(chart) {
chart.data.labels.forEach(function(e, i, a) {
if (/\n/.test(e)) {
a[i] = e.split(/\n/);
}
});
}
}]
add this followed by your chart options
ᴜꜱᴀɢᴇ :
add a new line character (\n) to your label, wherever you wish to add a line break.
ᴅᴇᴍᴏ
var chart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: ['Jan\n2017', 'Feb', 'Mar', 'Apr'],
datasets: [{
label: 'BAR',
data: [1, 2, 3, 4],
backgroundColor: 'rgba(0, 119, 290, 0.7)'
}]
},
options: {
scales: {
xAxes: [{
ticks: {
beginAtZero: true
}
}]
}
},
plugins: [{
beforeInit: function(chart) {
chart.data.labels.forEach(function(e, i, a) {
if (/\n/.test(e)) {
a[i] = e.split(/\n/);
}
});
}
}]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="ctx"></canvas>

Bar labels in Legend

My problem is similar to How to show bar labels in legend in Chart.js 2.1.6?
I want to have to same output a pie chart give, but I do not want to create multiple datasets. I managed to do this, but now I can't find how.
Here is my code sample :
var myChart = new Chart(ctx, {
type: type_p,
data: {
labels: ['Lundi','Mardi'],
datasets: [{
data: [50,20],
backgroundColor: color,
borderColor: color,
borderWidth: 1
}]
}
I want the same legend as a pie chart, but with a bar chart:
Is this a way to do this?
To accomplish this, you would have to generate custom labels (using generateLabels() function) based on the labels array of your dataset.
legend: {
labels: {
generateLabels: function(chart) {
var labels = chart.data.labels;
var dataset = chart.data.datasets[0];
var legend = labels.map(function(label, index) {
return {
datasetIndex: 0,
fillStyle: dataset.backgroundColor && dataset.backgroundColor[index],
strokeStyle: dataset.borderColor && dataset.borderColor[index],
lineWidth: dataset.borderWidth,
text: label
}
});
return legend;
}
}
}
add this in your chart options
ᴡᴏʀᴋɪɴɢ ᴇxᴀᴍᴘʟᴇ ⧩
var ctx = canvas.getContext('2d');
var chart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi'],
datasets: [{
data: [1, 2, 3, 4, 5],
backgroundColor: ['#ff6384', '#36a2eb', '#ffce56', '#4bc0c0', '#9966ff'],
borderColor: ['#ff6384', '#36a2eb', '#ffce56', '#4bc0c0', '#9966ff'],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
},
legend: {
labels: {
generateLabels: function(chart) {
var labels = chart.data.labels;
var dataset = chart.data.datasets[0];
var legend = labels.map(function(label, index) {
return {
datasetIndex: 0,
fillStyle: dataset.backgroundColor && dataset.backgroundColor[index],
strokeStyle: dataset.borderColor && dataset.borderColor[index],
lineWidth: dataset.borderWidth,
text: label
}
});
return legend;
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="canvas"></canvas>

Categories