Combine Chart.js programmatic and legend based dataset toggling - javascript

How can I make programmatic toggling of datasets play together with the one from ChartJS' legend clicking?
Here's a repro of my problem:
Run below snippet.
Toggle fruit data off with button: just works!
Toggle fruit data back on with button: just works!
Toggle fruit off by clicking the legend item: works.
Toggle fruit back on by clicking the legend item: works.
Toggle fruit again with the button: ⚠ broken!
I expect the custom logic to play nice with ChartJS' built in feature.
const chart = new Chart(document.getElementById("chart").getContext("2d"), {
type: "bar",
data: {
labels: ["label1", "label2", "label3"],
datasets: [
{ label: "Fruit", backgroundColor: "IndianRed", data: [1,2,3] },
{ label: "Veggies", backgroundColor: "LightGreen", data: [2,5,3] },
]
}
});
function togglestuff() {
const dataset = chart.data.datasets.find(ds => ds.label === "Fruit");
dataset.hidden = !dataset.hidden;
chart.update();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.2/chart.min.js"></script>
<div><canvas id="chart" height="75px"></canvas></div>
<button id="hide" onclick="togglestuff()">
Toggle fruit programmatically
</button>
What am I doing wrong?

Turns out hidden works, but there's also a property visible which then needs to be programmatically used with setDatasetVisibility(datasetIndex, visibility) .
So, one method would be:
Get dataset index with indexOf or similar
Get metadata at that index with chart.getDatasetMeta(index)
Use metadata.visible and pass it to setDatasetVisibility(...)
chart.update() as per usual
const chart = new Chart(document.getElementById("chart").getContext("2d"), {
type: "bar",
data: {
labels: ["label1", "label2", "label3"],
datasets: [
{ label: "Fruit", backgroundColor: "IndianRed", data: [1,2,3] },
{ label: "Veggies", backgroundColor: "LightGreen", data: [2,5,3] },
]
}
});
function togglestuff() {
const dataset = chart.data.datasets.find(ds => ds.label === "Fruit");
const index = chart.data.datasets.indexOf(dataset);
const metadata = chart.getDatasetMeta(index);
chart.setDatasetVisibility(
index,
!metadata.visible,
);
chart.update();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.2/chart.min.js"></script>
<div><canvas id="chart" height="75px"></canvas></div>
<button id="hide" onclick="togglestuff()">
Toggle fruit programmatically
</button>

Related

Set a limit / max number for legends

Lets say I have a Donut chart with 5 items in data like this
const data = {
labels: ['E-commerce', 'Enterprise', 'Green', 'Grey', 'Purple'],
datasets: [
{
label: '# of Votes',
data: [12, 19, 3, 5, 3],
backgroundColor: ['#C07CC3', '#9C3848', '#9DDBAD', '#ADA8B6', '#606EDA'],
borderWidth: 1,
},
],
}
I don't want it to show all the legends as I don't have space or whatever reason
How can I hide the Green and purple in this example?
I mean only from legends not from chart
I see two easy ways, how you could approach this problem:
(I personally I would use the second option below, it is more configurable, but it needs just abit of coding)
You can simply delete the labels you want to delete from the labels- array, and they won't show up in the legend.
But keep in mind you would have to change the order of the data and backgroundColor arrays, to match this change.
Here a short demo:
const data = {
labels: ['E-commerce', 'Enterprise', 'Grey'], // <-- just remove the unwanted labels
datasets: [{
data: [12, 19, 5, 3, 3], // <-- reorder
backgroundColor: ['#C07CC3', '#9C3848', '#ADA8B6', '#9DDBAD', '#606EDA'], // <-- reorder
borderWidth: 1,
}],
};
const config = {
type: 'doughnut',
data: data,
options: {
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
usePointStyle: true,
},
}
},
}
};
new Chart(
document.getElementById('chart'),
config
);
<script src="//cdn.jsdelivr.net/npm/chart.js"></script>
<div class="chart" style="height:184px; width:350px;">
<canvas id="chart" ></canvas>
</div>
Better and cleaner (but some coding is needed), you can filter out label-items, you don't want to display, with the function array filter. (details can be found in the documentation)
UPDATED Alternative Version Demo:
here only the Top 3 labels (limiting the amount of labels with the variable maxLabelsToShow) will be shown (sort order is descending, but changing this is would be easy)
function getLabelsOnlyTopX(num, data, labels){
let selectedLabels = []
//we don't want to alter the order
let helperData = [...data];
//sort in descending order
helperData.sort( (a,b) => b-a);
//get top X Values
helperData = helperData.slice(0, num);
//get index for the data
let indexes = data.map( (value, index) => ({value,index}) ).filter(item => helperData.some(n1 => n1 == item.value))
//slecet only labels with the correct index
selectedLabels = labels.filter((value, index) => indexes.some( n => n.index == index))
// just be sure that a maximum of num labels are sent
return selectedLabels.slice(0, num);
}
let maxLabelsToShow = 3;
let serverData = [12, 19, 3, 5, 3]
let labels = ['E-commerce', 'Enterprise', 'Green', 'Grey', 'Purple'];
// Calling the newly created function
let showOnly = getLabelsOnlyTopX(maxLabelsToShow, serverData, labels);
const data = {
labels: labels,
datasets: [{
data: serverData,
backgroundColor: ['#C07CC3', '#9C3848',
'#9DDBAD', '#ADA8B6', '#606EDA'],
borderWidth: 1,
}],
};
const config = {
type: 'doughnut',
data: data,
options: {
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
usePointStyle: true,
/* FILTER function */
filter: function(item, chart) {
return showOnly.indexOf( item.text) > -1;
}
},
}
},
}
};
new Chart(
document.getElementById('chart'),
config
);
<script src="//cdn.jsdelivr.net/npm/chart.js"></script>
<div class="chart" style="height:184px; width:350px;">
<canvas id="chart" ></canvas>
</div>

Chart.js: Label each dataset

I've been trying to make chart.js have a label for each dataset but the first label is located underneath both datasets and the second on an empty area of the chart.
var powerChart = new Chart(powerCanvas, {
type: "bar",
data: {
labels: ["Volts", "Amperes"],
datasets: [
{
label: "Volts", // This bar should have this label underneath itself
data: [24.78], // Initial value, will be overwritten
},
{
label: "Amperes", // This bar should have this label underneath itself
data: [14.51],
},
],
},
options: {},
});
See https://jsfiddle.net/w20c3tru/2/ for what I have tried and a working example.
Note: I tried to follow
Chart.js Line-Chart with different Labels for each Dataset
but could not see any difference.
Note the comments below:
var powerChart = new Chart(powerCanvas,
{
type: 'bar',
data:
{
labels: ['Volts', 'Amperes'],
datasets:
[
{
label: 'Value', // This is actually the label that shows up when you hover over a bar
data: [24.78, 14.51] // NOTE: Number of labels defined above should equal number of values here
},
]
},
options:
{
}
}
);
Edit:
data:
{
labels: ['Volts', 'Amperes'],
datasets:
[
{
data: [24.78, 14.51], // Volts and amperes, respectively
borderWidth: 1,
borderColor: ['#0000e0', '#e00000'], // Border colors for volts and amperes, respectively
},
]
},
Finally I have it working as I want thanks to a hint by a user who separated the data in x and y sections
Code on jsFiddle
Full working example here:
jsFiddle

How can I display the xAxes and yAxes data in the tooltip, Chart JS?

I am trying to achieve the Tooltip shown below in the first image. Inside of the tooltip I need to display the yAxes and xAxes data. The chart.js version I am using is 3.7.0
My tooltip looks like this:
The tooltip that I am trying copy:
The chart.js documentation is quite hard for me to understand. Is there any guru that can explain to me.
Question: Why is my tooltip returning the yAxes data, that I return as a variable(label) as undefined?
Are there any other options I can use to make my chart look like the chart in the second picture?
My Code:
tooltip: {
displayColors: false,
backgroundColor: 'rgba(45,132,180,0.8)',
bodyFontColor: 'rgb(255,255,255)',
callbacks: {
title: function(item, everything){
return;
},
label: function(item, everything){
//console.log(item);
//console.log(everything);
let first = item.yLabel;
let second = item.xLabel;
let label = first + ' ppm';
return label;
}
}
}
Thank you in advance for your time and efforts, please help me figure out what am I doing wrong!
yLabel and xLabel dont exist anymore on the tooltip, they are V2 syntax.
You can just axess the y object in the parsed section to get the y value. Then you can use the afterBody callback to show the x label like so:
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: {
displayColors: false,
backgroundColor: 'rgba(45,132,180,0.8)',
bodyFontColor: 'rgb(255,255,255)',
callbacks: {
title: () => {
return
},
label: (ttItem) => (`${ttItem.parsed.y} ppm`),
afterBody: (ttItems) => (ttItems[0].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.7.0/chart.js"></script>
</body>

charts js, doughnut chart not rendering tooptips correctly

i have a multilevel donut chart but it is not rendering correctly here is code
the problem is, onmouseover on all green parts it says objects, on all grey parts it says products, solution i would like is, on outer ring it should say products, in middle objects, and inner most should be materials, grey areas should just show number. here is a jsfiddle of the problem
Code:
var op=93;
var ap=99;
var mp=66;
var ctx = new Chart(myChart, {
type: 'doughnut',
data: {
labels: ['Objects', 'Products', 'Materials'],
datasets: [{
label: 'Objects',
data: [op, 100 - op],
backgroundColor: ['#006a4e','#eeeeee'],
hoverOffset: 4
},{
label: 'Products',
data: [ap, 100 - ap],
backgroundColor: ['#2e856e', '#eeeeee'],
hoverOffset: 4
},
{
label: 'Materials',
data: [mp, 100 - mp],
backgroundColor: ['#5ca08e', '#eeeeee'],
hoverOffset: 4
}
]
},
options: {
//cutoutPercentage: 40,
height: 200,
width:200
}
});
You can achieve that fairly simple with Chart.JS 2.7.2. Add labels to each dataset like this:
data: {
labels: ['Existing', 'Non'],
datasets: [
{
labels: ['Objects', 'Non-objects'],
...
}, {
labels: ['Products', 'Non-products'],
...
},
{
labels: ['Materials', 'Non-materials'],
...
}
]
}
And add the following label tooltip callback:
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var dataset = data.datasets[tooltipItem.datasetIndex];
var index = tooltipItem.index;
return dataset.labels[index] + ": " + dataset.data[index];
}
}
}
Demo: https://jsfiddle.net/adelriosantiago/fxd6vops/3/
I am sure it is possible with Chart.JS > 3.0 too but I have no idea how since quite a few things changed in the structure.
I had a same problem which took a lot of time but what worked in the end was importing these elements and initialising them ..
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
ChartJS.register(ArcElement, Tooltip, Legend);
these are the elements that helps make the entire chart. you can't skip them

Pushing data from json to Chart.js labels and data

I am using the Chart.js lib to make charts.
I have a json array that I am getting from a database.
Here is the console log of it: Data
I need to get the address, speed, and speed limit for every element in the list and use it in a chart.
My current code is as follows:
function ShowChart() {
var popCanvas = document.getElementById("speedLimitsChart");
console.dir(speeddata);
var labels = speeddata.map(function (e) {
return e.Adress;
});
var speed = speeddata.map(function (e) {
return e.Speed;
});
var speedlimits = speeddata.map(function (e) {
return e.SpeedLimits;
});
console.dir(labels);
var barChart = new Chart(popCanvas, {
type: 'bar',
data: {
datasets: [{
label: 'Speed',
data: speed,
backgroundColor: '#1E90FF'
}, {
label: 'Speed Limits',
data: speedlimits,
backgroundColor: '#B22222',
type: 'line'
}],
},
labels: labels
});
}
But in the result I only have the first element in my chart, and there are no labels.
Here is the output screen: Chart
I checked speed, speedlimits and labels and all of them have 2 elements. Can you please advise where my problem might be?
I found where is my problem
I need to write labels inside of data
Like this
var barChart = new Chart(popCanvas, {
type: 'bar',
data: {
labels: labels ,
datasets: [{
label: 'Speed',
data: speed,
backgroundColor: '#1E90FF'
}, {
label: 'Speed Limits',
data: speedlimits,
backgroundColor: '#B22222',
type: 'line'
}],
},
});

Categories

Resources