How to add a dataset toggle to Chart.js? - javascript

I'm using Chart.js to create a line chart. I would like to have four different datasets that will all be visibile by default, but can be toggled on and off by clicking a button. How can this be achieved? I can't seem to find an answer in the documentation. .addData(), .removeData() and .update() all seem to be used for adding or removing values to existing datasets, but not adding or removing entire datasets. I would think this would be fairly commonly used feature but I can't find an answer anywhere.

After thoroughly researching this, there doesn't appear to be any built in function to toggle entire datasets. I used the .destroy() function to remove the entire existing chart, and then some logic to redraw it with the necessary datasets.
EDIT: Here's a fiddle with my full code if it's helpful to anyone -> http://jsfiddle.net/21xg27kr/4/

Here is a line chart with two datasets. By updating the datasets and calling the .update() method. The benefit here is you don't need to destroy the whole chart, and there is a nice animated transition which can be disabled.
TL:DR; solution on jsfiddle
Step by Step:
Bring in Chart.js from a CDN
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
Create the HTML Canvas element that will hold the chart
<canvas id="line-chart"></canvas>
Hide/Show buttons for this example
Creating the chart, and the functions to update it live - notice that the same integer data needs to be copied in two places - in the initial creation, and in the show function.
<script>
lineChart = new Chart(document.getElementById("line-chart"), {
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [
{
label: "Set 1",
fill: true,
backgroundColor: "rgba(90,181,242,0.2)",
borderColor: "rgba(179,181,198,1)",
pointBorderColor: "#fff",
pointBackgroundColor: "rgba(179,181,198,1)",
data: [3, 1, 1, 0]
}, {
label: "Set 2",
fill: true,
backgroundColor: "rgba(255,99,132,0.2)",
borderColor: "rgba(255,99,132,1)",
pointBorderColor: "#fff",
pointBackgroundColor: "rgba(255,99,132,1)",
pointBorderColor: "#fff",
data: [1, 3, 3, 5]
}
]
},
options: {
title: {
display: true,
text: 'Chart Title'
}
}
});
function restoreLayer2(){
lineChart.data.datasets[1].data = [1, 3, 3, 5];
lineChart.update();
}
function removeLayer2() {
lineChart.data.datasets[1].data = [];
lineChart.update();
}
</script>

Related

Chart.js: Change lengend colors to array

I have a chart with three datasets (Svelte REPL with code and result) and would like to change the background colors of the boxes corresponding to each set, for example:
let legendColors = ['#dddddd', '#aaaaaa', '#777777'];
After several attempts the closest I got was changing font color, but not even that as array, only a single one. :(
How do I get the boxes of the legend to be in the 3 colors I want?
Thank you in advance for your help!
Edit: I did find something here: What happened to generateLegend() in chartjs 3.0?, but that removes the functionality of the legend (i.e. removing parts of the data).
Edit: Another answer below has running snippet that is simpler to implement which also answers OP's request. My original answer below is more focused on building the legend completely by oneself.
Live Demo. I've found this is fun...
Some points to note:
You need to build the legend yourself, as your colors are not "relevant" to the data you use (that's why by default it is using the colors from dataset "a") (edited as another answer shows such possibility)
As you are using Svelte, in the Demo I used the <style> which binds with the <div> for the legend for styling of the legend.
For people who ain't using Svelte, or are seeking solutions within Chart.js, they should follow the advice from the StackOverflow answer OP linked -- they need to build the legend using the plugins field of 2nd argument when creating a new Chart()
No matter which route to take, to keep the functionality of the legend as OP mentioned, I added a tweaked version of default behaviour of OnClick.
P.S. For (3) we have to further override generateLabels() (here, but may cause meta.controllers._resolveAnimation having problems, which we may still need to override onClick).
P.P.S. When a legend item is clicked, the label is crossed out (strikethrough) as a default behaviour. That is left as a further exercise or another answer. (For Svelte, use Boolean flags to change styles. Alternatively, hack with CSS checked checkbox labels).
You can achieve this in 2 ways, using a custom generateLabels function or by specifying a backgroundcolor in the dataset.
Custom generateLabels:
let legendColors = ['#dddddd', '#aaaaaa'];
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'
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderColor: 'orange'
}
]
},
options: {
plugins: {
legend: {
labels: {
generateLabels: function(chart) {
const datasets = chart.data.datasets;
const {
labels: {
usePointStyle,
pointStyle,
textAlign,
color
}
} = chart.legend.options;
return chart._getSortedDatasetMetas().map((meta, i) => {
const style = meta.controller.getStyle(usePointStyle ? 0 : undefined);
const borderWidth = Chart.helpers.toPadding(style.borderWidth);
return {
text: datasets[meta.index].label,
fillStyle: legendColors[i],
fontColor: color,
hidden: !meta.visible,
lineCap: style.borderCapStyle,
lineDash: style.borderDash,
lineDashOffset: style.borderDashOffset,
lineJoin: style.borderJoinStyle,
lineWidth: (borderWidth.width + borderWidth.height) / 4,
strokeStyle: style.borderColor,
pointStyle: pointStyle || style.pointStyle,
rotation: style.rotation,
textAlign: textAlign || style.textAlign,
borderRadius: 0, // TODO: v4, default to style.borderRadius
// Below is extra data used for toggling the datasets
datasetIndex: meta.index
};
}, this);
}
}
}
}
},
}
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.7.1/chart.js"></script>
</body>
Adding background color prop in datasets:
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',
pointBackgroundColor: 'pink',
backgroundColor: '#dddddd'
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderColor: 'orange',
pointBackgroundColor: 'orange',
backgroundColor: '#aaaaaa'
}
]
},
options: {},
}
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.7.1/chart.js"></script>
</body>

How do I display a different chartjs tooltip title?

I am displaying a mixed bar and line chart using chartjs. I have successfully managed to display different content in the tooltip when your hover over the line chart, or the bars, but using the same condition does not work to display a different title:
callbacks: {
title: function(tooltipItem, data) {
if (tooltipItem.datasetIndex === 0){
return data['datasets'][0]['label'];
}else{
return data['datasets'][1]['label'];
}
}
}
I am using PHP values to populate data into the main chart. Here is the code for the main chart:
var ctx_<?=$chartname?> = document.getElementById('<?=$chartname?>').getContext('2d');
new Chart(ctx_<?=$chartname?>, {
type: 'bar',
data: {
labels: [<?=$labels?>], //label array needs to go here
datasets: [{
label: 'Overall Average Score',
notes: ['','','','',''],
data: [<?=$averages?>],
pointBackgroundColor: '#6600CC',
pointRadius: 6,
pointBorderColor: 'rgb(255, 255, 255)',
pointHoverRadius:8,
pointHoverBorderWidth: 2,
pointHoverBorderColor: 'rgb(255, 255, 255)',
fill: false,
// Changes this dataset to become a line
type: 'line'
},{
label: '<?=$forename?>\'s Score',
notes: [<?=$notes?>],
data: [<?=$scores?>] //data array needs to go in here
}],
},
options: groupBarChartOptions
});
However, instead of populating the tooltip title as 'Overall Average Score' it populates the line graph tooltip title of 'Pupil Name's Score'
I am using the same
if (tooltipItem.datasetIndex === 0){
condition in the following label callback, and it works.
This should work for what you want:
title: (tooltipItem, data) => {
return data['datasets'][tooltipItem[0].datasetIndex]['label']
}
working example: https://jsfiddle.net/Leelenaleee/bcwky40z/4/

Why can I not see a data value when hovering over a point on the radar chart?

I have created a Radar Chart using ChartJS as follows:
HTML:
<canvas id="radarChartTest" width="800" height="600"></canvas>
<script>
radarChartTest(["A", "B", "C", "D"], [5, 10, 15, 20], document.getElementById("radarChartTest"));
</script>
JS:
function radarChartTest(categories, totals, chartToPopulate) {
var chartDisplay = chartToPopulate;
var newChart = new Chart(chartDisplay, {
type: 'radar',
data: {
labels: categories,
datasets: [
{
data: totals,
label: "test"
}
]
}
})
}
JSFiddle
The chart draws and populates fine. However, when I hover over a radar point, it does not display the value:
There should be a number after test:.
I am expecting something similar to this:
Am I missing an attribute or something? I have checked the documentation but could not spot anything for it. I have also compared my code to where I found (a working example), but could not spot anything there either.
This is due to a bug in the that version (2.8.0) of Chart.js (Values on Tooltip of Radar Chart is not shown).
You can fix it by upgrading to 2.9.0.
Updated working Fiddle with version 2.9.0
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.0"> </script>
As far as I can tell I had the same problem a while ago, maybe, because I'll need to research a little more about this issue, the problem is due to a previous version of Chart.js. In any case here is a CodePen with a solution to your problem RadarChart Example. Hope this helps. Cheers, sigfried.
UPDATE
Since this is not only about the answers here is the link from the documentation of Chart.js that I used to solve the tooltips issue Label Callbacks.
I tried to re-create the problem you have but it looks all good to me.(A number after "total")
Since you only post part of your code, I am not sure where you are confused.
So I attached the full code I created here. Compare it to your code and let me know if you still can't generate the figure you want.
<!DOCTYPE html>
<html>
<body>
<h2>JavaScript chart</h2>
<p>Radar Chart Demo </p>
<canvas id="radar-chart" width="800" height="600"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js">
</script>
<script>
function radarChart(categories, totals, averages, chartToPopulate, chartTitle) {
var chartDisplay = chartToPopulate;
var newChart = new Chart(chartDisplay, {
type: 'radar',
data: {
labels: categories,
datasets: [
{
data: averages,
pointBackgroundColor: ["rgba(199,54,54,1)", "rgba(199,54,122,1)", "rgba(199,54,154,1)", "rgba(146,54,199,1)", "rgba(69,54,199,1)", "rgba(54,103,199,1)", "rgba(54,165,199,1)", "rgba(54,199,180,1)"],
pointBorderColor: ["rgba(199,54,54,1)", "rgba(199,54,122,1)", "rgba(199,54,154,1)", "rgba(146,54,199,1)", "rgba(69,54,199,1)", "rgba(54,103,199,1)", "rgba(54,165,199,1)", "rgba(54,199,180,1)"],
pointRadius: 15,
pointStyle: "cross",
pointHoverRadius: 25,
pointHoverBorderWidth: 3,
pointRotation: 45,
pointBorderWidth: 1.2,
backgroundColor: "rgba(61,49,225,0.5)",
borderColor: "rgba(61,49,225,1)",
label: "Averages",
fill: true
},
{
data: totals,
pointBackgroundColor: ["rgba(199,54,54,1)", "rgba(199,54,122,1)", "rgba(199,54,154,1)", "rgba(146,54,199,1)", "rgba(69,54,199,1)", "rgba(54,103,199,1)", "rgba(54,165,199,1)", "rgba(54,199,180,1)"],
pointBorderColor: ["rgba(199,54,54,1)", "rgba(199,54,122,1)", "rgba(199,54,154,1)", "rgba(146,54,199,1)", "rgba(69,54,199,1)", "rgba(54,103,199,1)", "rgba(54,165,199,1)", "rgba(54,199,180,1)"],
pointRadius: 15,
pointStyle: "cross",
pointHoverRadius: 25,
pointHoverBorderWidth: 3,
pointRotation: 45,
pointBorderWidth: 1.2,
backgroundColor: "rgba(225,49,52,0.35)",
borderColor: "rgba(225,49,52,1)",
label: "Totals",
fill: true
},
]
},
options: {
maintainAspectRation: false,
title: {
display: true,
text: chartTitle,
fontSize: 16
}
}
})
return chartDisplay;
}
var Chart = radarChart(["1","2","3","4"], [100,200,300,400], [10,20,30,40],
document.getElementById("radar-chart"), "TEST")
document.getElementById("radar-chart").innerHTML = Chart;
</script>
</body>
</html>
I am using the version 2.8, and i got the same problem, to fix
tooltips: {
enabled: true,
callbacks: {
label: function(tooltipItem, data) {
return data.datasets[tooltipItem.datasetIndex].label + ' : ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
}
}
}
See the issue
https://github.com/chartjs/Chart.js/issues/6188#issuecomment-497251833

How to remove the inside-border from doughnut chart? Chart.js

I want to remove the marked white line, is there any option to do that? If not, how can one achieve this without painting over canvas?
You can set borderWidth property to 0.
options: {
elements: {
arc: {
borderWidth: 0
}
}
}
jsfiddle
Update
If you just want to remove only one border then you can do this using following code
datasets: [{
data: [1, 2, 3, 4],
backgroundColor: ["#BDC3C7","#9B59B6","#E74C3C","#26B99A"],
borderWidth: [0, 1, 1, 0]
}]
jsfiddle

ChartJS – How to show border on empty pie chart?

I have a couple of pie/doughnut charts displayed using ChartJS. Usually, they contain data like [200, 1200, 300] and [30, 500] to give an estimate, but at rare occasions, they will contain [0, 0, 0] and [0, 0]. The problem then, is that the chart disappears, even though I have a border enabled. I have solved this by adding dummy values to the first element in the arrays, but I don't like how the code looks and want a better way.
Is it possible to make the border visible (a circle) when the array contains only zeroes?
Edit:
I don't seem to get any answers to this question. Is it considered a bug when the border is not showing on empty data or is this intended behavior? I don't know if anyone else has had this problem. I still need to find an elegant way to deal with this issue, and I haven't found one yet.
This is not a bug, as the borders are rendered per data item. If all the data is 0s, then every slice has no width, so no border can show (the only other way to handle this scenario for ChartJS would be to give each item equal sizing, which isn't better).
You can add a dummy value without a label and filter the legend and tooltips so that the data is treated like a blank space to the user. In the example below, a check before rendering the chart ensures that if all data points are 0, a data point with a value of 1 is added to the chart with no label. Two datasets are shown to highlight the difference.
let ctx = document.getElementById('chartContainer').getContext('2d');
let data = [[0, 0, 0], [1,2,3]];
let labels = ["A", "B", "C"];
let bgColors = ['yellow', 'orange', 'aquamarine'];
let options = {
borderWidth: 1,
borderColor: 'black',
legend: {
labels: {
// Prevent items with undefined labels from appearing in the legend
filter: (item) => item.text !== undefined
}
},
tooltips: {
// Prevent items with undefined labels from showing tooltips
filter: (item, chart) => chart.labels[item.index] !== undefined
}
}
let chartConfig = {
type: 'pie',
data: {
labels: labels,
datasets: [{
data: data[0],
backgroundColor: bgColors,
label: "data",
borderColor: 'black',
borderWidth: 2
}, {
data: data[1],
backgroundColor: bgColors,
label: "data",
borderColor: 'black',
borderWidth: 2
}]
},
options: options
}
// Check if data is all 0s; if it is, add dummy data to end with empty label
chartConfig.data.datasets.forEach(dataset => {
if (dataset.data.every(el => el === 0)) {
dataset.backgroundColor.push('rgba(255,255,255,0)');
dataset.data.push(1);
}
})
let pieChart = new Chart(ctx, chartConfig);
.chartContainer {
height: 200px;
width: 200px;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0/dist/Chart.min.js"></script>
<div class="chartContainer">
<canvas id="chartContainer" width="200" height="200"></canvas>
</div>

Categories