Reputation: 33
I want to show visualization of machines downtime for 2 shifts - day (12-hr) and night (12-hr) for 30 days. Thus, I use the stacked bar chart with groups and it looks good accept that I don't want to have the legends to show both shifts (day & night).
<canvas id="myChart"></canvas>
<script>
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30",],
datasets: [{
label: 'Machine 1 - Day',
stack: 'Stack 0',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF4A4A',
},
{
label: 'Machine 1 - Night',
stack: 'Stack 1',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF4A4A',
},
{
label: 'Machine 2 - Day',
stack: 'Stack 0',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF9C2A',
},
{
label: 'Machine 2 - Night',
stack: 'Stack 1',
data: [12, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF9C2A',
},
]
},
options: {
plugins: {
legend: {
display: true
}
},
scales: {
xAxes: [{
stacked: true,
}],
yAxes: [{
stacked: true
}]
}
}
});
</script>
How do I combine the machine 1 or 2 for day & night shift in the legend and clicking machine 1 or 2 will hide both shifts (day & night)
stacked bar chart with groups edit Legend
I found the method below that might work for me. https://www.chartjs.org/docs/3.1.0/configuration/legend.html#custom-on-click-actions
click handler of the first two datasets
How do I proceed for second two datasets of the click handler and so on accordingly.
<canvas id="myChart"></canvas>
<script>
var defaultLegendClickHandler = Chart.defaults.plugins.legend.onClick;
var newLegendClickHandler = function (e, legendItem, legend) {
var index = legendItem.datasetIndex;
if (index > 1) {
// Do the original logic
defaultLegendClickHandler(e, legendItem);
} else {
let ci = legend.chart;
[
ci.getDatasetMeta(0),
ci.getDatasetMeta(1)
].forEach(function (meta) {
meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;
});
ci.update();
}
};
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30",],
datasets: [{
label: 'Machine 1 - Day',
stack: 'Stack 0',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF4A4A',
},
{
label: 'Machine 1 - Night',
stack: 'Stack 1',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF4A4A',
},
{
label: 'Machine 2 - Day',
stack: 'Stack 0',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF9C2A',
},
{
label: 'Machine 2 - Night',
stack: 'Stack 1',
data: [12, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF9C2A',
},
]
},
options: {
plugins: {
legend: {
onClick: newLegendClickHandler
}
},
scales: {
xAxes: [{
stacked: true,
}],
yAxes: [{
stacked: true
}]
}
}
});
</script>
Upvotes: 3
Views: 3214
Reputation: 1
Hey I got something like this working, but I am hiding everything with the same stack variable and hiding certain data series as well so I can hide them without having their legend items available for click:
I have a lot of series that have the same datasets (ie, failed numbers, retested numbers and dont want to show those labels)
// Get Data From Views and Add Custom onClick Listener
let tempData = { ...views[i].data };
if (
// Kill the onClick and add a much slower but grouping onClick, hide all with part number in stack
tempData &&
tempData.options &&
tempData.options.plugins &&
tempData.options.plugins.legend
)
tempData.options.plugins.legend.onClick = (e, legendItem) => {
// My labels always start with the part number and a space so I grab that.
const partName = legendItem.text.split(' ')[0];
const chart = Chart.getChart('temp-chart');
// Toggle the data set that was clicked on
const datasetIndex = legendItem.datasetIndex;
if (!chart.getDatasetMeta(datasetIndex).hidden) chart.hide(datasetIndex);
else chart.show(datasetIndex);
let j = 0;
// Iterate through all datasets, cap at 200 for safety
while (chart.getDatasetMeta(j).stack && j < 200) {
const datasetMeta = chart.getDatasetMeta(j);
const stack = datasetMeta.stack;
const datasetName = datasetMeta.label;
// I add a stack id to all my datasets and use that to determine what is 'grouped'
// I have a plugin that does not render any datasets which do not have a label
// If the stack group matches and the dataset does not have a label it will be hidden.
if (
stack === partName &&
(datasetName == null ||
datasetName === '' ||
datasetName === 'undefined')
) {
if (!chart.getDatasetMeta(j).hidden) chart.hide(j);
else chart.show(j);
}
j++;
}
};
new Chart('temp-chart', { ...tempData });
And the legend plugin to hide legend items with no label.
Chart.defaults.plugins.legend.labels.filter = (item) => {
return item.text != null;
};
Upvotes: 0
Reputation: 26150
First you need to define a legend.labels.generateLabels function that filter out undesired legend labels and changes the text of the remaining ones.
generateLabels: chart => chart.data.datasets.map((ds, i) => ({
text: ds.label.substring(0, ds.label.indexOf('-')),
datasetIndex: i,
fillStyle: chart.data.datasets[i].backgroundColor,
strokeStyle: chart.data.datasets[i].backgroundColor,
hidden: chart.getDatasetMeta(i).hidden
}))
.filter((ds, i) => i % 2),
Then you'll have to define a legend.onClick
function that takes care of also showing/hiding the sibling dataset when the user clicks on a legend label.
onClick: (event, legendItem, legend) => {
let hidden = !legend.chart.getDatasetMeta(legendItem.datasetIndex).hidden;
(legendItem.datasetIndex == 1 ? [0, 1] : [2, 3])
.forEach(i => legend.chart.getDatasetMeta(i).hidden = hidden);
legend.chart.update();
}
For further details, see the Legend and API chapters of the Chart.js documentation.
Please take a look at your amended code and see how it works.
new Chart('chart', {
type: 'bar',
data: {
labels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", ],
datasets: [{
label: 'Machine 1 - Day',
stack: 'Stack 0',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF4A4A',
},
{
label: 'Machine 1 - Night',
stack: 'Stack 1',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF4A4A',
},
{
label: 'Machine 2 - Day',
stack: 'Stack 0',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF9C2A',
},
{
label: 'Machine 2 - Night',
stack: 'Stack 1',
data: [12, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF9C2A',
}
]
},
options: {
plugins: {
legend: {
labels: {
generateLabels: chart => chart.data.datasets.map((ds, i) => ({
text: ds.label.substring(0, ds.label.indexOf('-')),
datasetIndex: i,
fillStyle: chart.data.datasets[i].backgroundColor,
strokeStyle: chart.data.datasets[i].backgroundColor,
hidden: chart.getDatasetMeta(i).hidden
}))
.filter((ds, i) => i % 2),
},
onClick: (event, legendItem, legend) => {
let hidden = !legend.chart.getDatasetMeta(legendItem.datasetIndex).hidden;
(legendItem.datasetIndex == 1 ? [0, 1] : [2, 3])
.forEach(i => legend.chart.getDatasetMeta(i).hidden = hidden);
legend.chart.update();
}
}
},
scales: {
x: {
stacked: true,
},
y: {
stacked: true
}
}
}
});
canvas {
max-height: 190px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.js"></script>
<canvas id="chart"></canvas>
Upvotes: 5