ChartJS: Adjust Tooltip with sums in body - javascript

I have the following output at the moment:
However, I would like to have the following table:
Minimum: 90
25th: 95
25th to Median: 100
(the blue one I dont want to show)
75th: 105
Max: 110
I have the following code, I hope this will be enough:
const data = {
labels: [
REMockProducts[0].valuations[0].short,
REMockProducts[0].valuations[1].short,
REMockProducts[0].valuations[2].short,
REMockProducts[0].valuations[3].short,
REMockProducts[0].valuations[4].short,
REMockProducts[0].valuations[5].short,
REMockProducts[0].valuations[6].short,
],
datasets: [
{
label: "Minimum",
data: [
REMockProducts[0].valuations[0].min,
REMockProducts[0].valuations[1].min,
REMockProducts[0].valuations[2].min,
REMockProducts[0].valuations[3].min,
REMockProducts[0].valuations[4].min,
REMockProducts[0].valuations[5].min,
REMockProducts[0].valuations[6].min,
],
toCheck: 0,
},
{
label: "25th",
data: [
lwpercDistance(0, 0)[0],
lwpercDistance(0, 1)[0],
lwpercDistance(0, 2)[0],
lwpercDistance(0, 3)[0],
lwpercDistance(0, 4)[0],
lwpercDistance(0, 5)[0],
lwpercDistance(0, 6)[0],
],
toCheck: 1,
},
{
label: "25th to Median",
data: [
medianDistance(0, 0)[0],
medianDistance(0, 1)[0],
medianDistance(0, 2)[0],
medianDistance(0, 3)[0],
medianDistance(0, 4)[0],
medianDistance(0, 5)[0],
medianDistance(0, 6)[0],
],
toCheck: 2,
},
{
label: false,
data: [0, 0, 0, 0, 0, 0, 0],
toCheck: 3,
},
{
label: "75th",
data: [
hipercDistance(0, 0)[0],
hipercDistance(0, 1)[0],
hipercDistance(0, 2)[0],
hipercDistance(0, 3)[0],
hipercDistance(0, 4)[0],
hipercDistance(0, 5)[0],
hipercDistance(0, 6)[0],
],
toCheck: 4,
},
{
label: "Max",
data: [
maxDistance(0, 0)[0],
maxDistance(0, 1)[0],
maxDistance(0, 2)[0],
maxDistance(0, 3)[0],
maxDistance(0, 4)[0],
maxDistance(0, 5)[0],
maxDistance(0, 6)[0],
],
toCheck: 5
},
],
};
[...] <Bar
data={data}
options={{
plugins: {
legend: {
position: "top",
},
tooltips: {
callbacks: {
label : function(data, tooltip) {
let k = 0;
let j = 0;
for (k; k < 5; k++) {
for (j; j < 7; j++) {
if (k > 0) {
let tooltip =
data.datasets[0].data[j] + data.datasets[k].data[j];
} else {
let tooltip = data.datasets[0].data[j];
}
}
return { text: tooltip };
}
},
},
},
},
indexAxis: "y",
scales: {
x: {
stacked: true,
ticks: {
autoSkip: false,
},
},
y: {
stacked: true,
ticks: {
autoSkip: false,
},
},
},
}}
/>
It is probably pretty clear that I am inexperiences with nested loops in JS.
The basic idea was to run through each dataset and either take the face value of the "Minimum" data or add them up.
Hovering over my tooltip variable actually shows the following:
I clearly must make an obvious mistake I can't figure out.
Links that helped me so far:
ChartJs different data for Tooltips
If condition not working for ChartJS
[Edit] I actually just realized that I did the sum incorrectly. Doesn't change much though

Basically your question is two parts.
Filter the 0 values
Add accumulative sum to each datapoint in tooltip
I simplified your code as you had a for loop which was not needed as it could be done all in Chart JS tooltip.
I created a video with the entire breakdown and explanation. You can watch here for more understanding: ChartJS: Adjust Tooltip with sums in body
See code below:
<script>
// setup
const data = {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255, 99, 132, 1)',
borderWidth: 1
},{
label: '# of Cost',
data: [11, 4, 0, 7, 10, 13],
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
},{
label: '# of Sales',
data: [10, 0, 10, 0, 10, 22],
backgroundColor:
'rgba(255, 206, 86, 0.2)',
borderColor: 'rgba(255, 206, 86, 1)',
borderWidth: 1
}]
};
// tooltip block
const tooltip = {
yAlign: 'bottom',
filter: function filterZeroData(datapoint) {
return datapoint.raw > 0;
},
callbacks: {
label: function(context) {
const stackedBarArray = [];
for (i = 0; i <= context.datasetIndex; i++){
stackedBarArray.push(context.parsed._stacks.y[i]);
};
console.log(stackedBarArray);
// reduce array
const reducer = (accumulator, currentValue) => accumulator + currentValue;
// return value in tooltip
console.log(context.dataset.label)
const labelName = context.dataset.label;
const labelValue = stackedBarArray.reduce(reducer);
return `${labelName} ${labelValue}`;
}
}
};
// config
const config = {
type: 'bar',
data,
options: {
plugins: {
tooltip,
},
scales: {
x: {
stacked: true
},
y: {
stacked: true,
beginAtZero: true
}
}
}
};
// render init block
const myChart = new Chart(
document.getElementById('myChart'),
config
);
</script>

Related

Setting a dynamic min and max date in CHARTJS using date-fn adapter

I am currently working on an analytic dashboard, and I have chosen Chartjs to visualize the graphs, everything works fine except I do not seem to know how to set the MIN and MAX date of the chartjs time object dynamically. It is been hardcoded like this:
const config = {
type: "bar",
data: {
datasets: [
{
label: "base",
data: monthly_data,
backgroundColor: ["rgba(255, 99, 132, 0.2)"],
borderColor: ["rgba(255, 99, 132, 1)"],
borderWidth: 1,
minBarLength: 7,
parsing: {
xAxisKey: "x",
yAxisKey: "base",
},
},
{
label: "Case",
data: monthly_data,
backgroundColor: ["rgba(54, 162, 235, 0.2)"],
borderColor: ["rgba(54, 162, 235, 1)"],
borderWidth: 1,
minBarLength: 7,
parsing: {
xAxisKey: "x",
yAxisKey: "case",
},
},
{
label: "Standard",
data: monthly_data,
backgroundColor: ["rgba(255, 206, 86, 0.2)"],
borderColor: ["rgba(255, 206, 86, 1)"],
borderWidth: 1,
minBarLength: 7,
parsing: {
xAxisKey: "x",
yAxisKey: "standard",
},
},
],
},
options: {
scales: {
x: {
type: "time",
min: "2021-03-01",
max: "2022-03-31",
time: {
unit: "month",
},
},
},
},
};
Can I call a function that can set this dynamically like for example in a monthly view the chart should show 12 months back?
you can just pass a function to the min and max that calculates the min and max for you like so:
options: {
scales: {
x: {
type: 'time',
min: () => {
return calculateMin()
}
}
}
}

chart.js remove lower grid from mixed chart

I have a mixed chart (scatter and line), hence I need 2 xAxis.
There's an extra grid that appears on the bottom of the chart, because that's where the other x axis normally has its labels (I removed them because I don't need them)
I'm trying to get rid of that extra grid, but I can't find how to do it. I've tried to specify the ticks color as white but the grid's still there.
Look at the image, it's that solitary extra grid at the bottom (marked with an horizontal bracket):
This is my code:
const ctx = document.getElementById('myChart');
const labels1 = ['A','R','Fyo','A-MAC','HR','Do','Rs','Dpr','GM','GF','EPK','EPS','Is1','Is2','Is3','FP','INVAR','INVER'];
const lasIs = (ctx, value) => ctx.p0DataIndex > 11 ? value: undefined;
const markedIdxs = [0,5,10,15,20,25,30,35,44, //... and many more];
const markedVals = [0,5,10,15,20,25,30,35,10,// ... and many more];
const data1 = {
labels: labels1,
datasets: [
{
type: 'line',
label: 'Line Dataset',
data: [65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65],
pointRadius: 0,
borderWidth: 1,
borderColor: '#0070C5',
xAxisID: 'x2'
},
{
type: 'line',
label: 'Line Dataset',
data: [50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50],
pointRadius: 0,
borderWidth: 1,
borderColor: '#0070C5',
xAxisID: 'x2'
},
{
type: 'line',
label: 'Line Dataset',
data: [102, 38, 54, 56, 102, 38, 54, 56, 102, 38, 54, 56, 102, 38, 54, 87, 62, 91],
pointStyle: 'circle',
pointRadius: 5,
borderWidth: 2,
borderColor: 'rgba(0, 120, 161,0.5)',
backgroundColor: 'rgba(0, 120, 161,0.5)',
fill: true,
segment: {
borderColor: ctx => lasIs (ctx, 'rgba(0, 168, 172, 0.5'),
backgroundColor: ctx => lasIs (ctx, 'rgba(0, 168, 172, 0.5'),
},
xAxisID: 'x2'
},
{
type: 'scatter',
pointStyle: 'dash',
pointRadius: 6,
borderColor: 'rgba(0, 0, 255,0.2)',
xAxisID: 'x',
data: [{x:1, y:36}, {x:1, y:37}, {x:1, y:39}, {x:1, y:40}, {x:1, y:42}... //and many more]
},
],
};
const multiplos5 = {
id: 'multiplos5',
afterDatasetsDraw(chart, args, options) {
const {ctx} = chart;
ctx.save();
ctx.font = "8px Verdana";
ctx.fillStyle = "#0070C5";
yaSeDibujo=84;
paddLeft = 0;
for (i=0; i < markedIdxs.length; i++) {
if (markedVals[i] < 10)
paddLeft=8;
else
paddLeft=12;
ctx.fillText(markedVals[i], chart.getDatasetMeta(3).data[markedIdxs[i]].x - paddLeft, chart.getDatasetMeta(3).data[markedIdxs[i]].y + 3);
}
}
};
Chart.register(multiplos5);
const myChart = new Chart(ctx, {
data: data1,
options: {
scales: {
x2: { // add extra axes
min: 0,
max: 18,
position: 'bottom',
type: 'category',
ticks: {color: '#0070C5'},
offset: true
},
x: {
min: 1,
max: 18,
ticks: {stepSize: 1, display: false},
offset: true
},
y: {
min: 20,
max: 120,
ticks: {stepSize: 10, color: '#555555', crossAlign: 'start'},
grid:{color:'#f1f1f1', display:true},
},
y2: {
position: 'right',
min: 20,
max: 120,
ticks: {stepSize: 10, color: '#555555'},
grid:{display:false}
},
},
plugins: {
multiplos5,
legend: false,
tooltip: {
displayColors: false,
filter: function (tooltipItem) {
return tooltipItem.datasetIndex == 2;
},
callbacks: {
//title: "el título",
label: function(context) {
let label = labels2[context.dataIndex] + ': ' + context.parsed.y;
return label;
}
}
}
},
},
});
Chart.js version is 3.6.2
Any help/workaround will be really appreciated.
Regards!!
Forgot to mention that the axis I'm talking about is 'x' (xAxisID: 'x')
In your config for the X axis where you set the ticks to display false, if you instead do this in the root of the X scale config it will hide that line so you will get:
scales:{
x: {
display: false,
min: 1,
max: 18,
offset: true
}
}

how to highlight stack bar in ChartJS with onclick

Is it possible to select a column in a stack bar chart with a colored border? Like this
I started from here:
https://jsfiddle.net/sdfx/hwx9awgn/
// Return with commas in between
var numberWithCommas = function(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
var dataPack1 = [40, 47, 44, 38, 27, 31, 25];
var dataPack2 = [10, 12, 7, 5, 4, 6, 8];
var dataPack3 = [17, 11, 22, 18, 12, 7, 5];
var dates = ["SN 1.0.1", "SN 1.0.2", "SN 1.0.3", "SN 1.0.4", "SN 1.0.5", "SN 2.0.0", "SN 2.0.1"];
// Chart.defaults.global.elements.rectangle.backgroundColor = '#FF0000';
var bar_ctx = document.getElementById('bar-chart');
var bar_chart = new Chart(bar_ctx, {
type: 'bar',
data: {
labels: dates,
datasets: [{
label: 'Bad Style',
data: dataPack1,
backgroundColor: "#512DA8",
hoverBackgroundColor: "#7E57C2",
hoverBorderWidth: 0
}, {
label: 'Warning',
data: dataPack2,
backgroundColor: "#FFA000",
hoverBackgroundColor: "#FFCA28",
hoverBorderWidth: 0
}, {
label: 'Error',
data: dataPack3,
backgroundColor: "#D32F2F",
hoverBackgroundColor: "#EF5350",
hoverBorderWidth: 0
}, ]
},
options: {
animation: {
duration: 10,
},
tooltips: {
mode: 'label',
callbacks: {
label: function(tooltipItem, data) {
return data.datasets[tooltipItem.datasetIndex].label + ": " + numberWithCommas(tooltipItem.yLabel);
}
}
},
scales: {
xAxes: [{
stacked: true,
gridLines: {
display: false
},
}],
yAxes: [{
stacked: true,
ticks: {
callback: function(value) {
return numberWithCommas(value);
},
},
}],
}, // scales legend: {display: true} } // options } );
Yes what you can do is in the onClick as second argument you get an array with all the active elements. So what you can do is loop over each dataset in your chart and see if the index matches one of the list with active elements.
If this is the case you give it a borderWidth of 1, else you put it to 0. After you did this you call chart variable.update();
To make this work you need to remove the hoverBorderWidth of all datasets and give all datasets a borderColor of yellow
Use attribute borderColor with an array and borderWith with and object.
datasets: [
{
label: 'Bad Style',
data: dataPack1,
backgroundColor: "#512DA8",
borderColor: [
'rgb(243, 255, 51)',
'rgb(81,45,168)',
'rgb(243, 255, 51)',
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
],
borderWidth: {
top: 0,
right: 4,
bottom: 0,
left: 4
},
},
rgb(243, 255, 51) is yellow color.
To change the color, try this:
var borderColor = [
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
'rgb(81,45,168)',
];
var bar_ctx = document.getElementById('bar-chart');
var bar_chart = new Chart(bar_ctx, {
type: 'bar',
data: {
labels: dates,
datasets: [
{
label: 'Bad Style',
data: dataPack1,
backgroundColor: "#512DA8",
hoverBackgroundColor: "#7E57C2",
hoverBorderWidth: 0,
borderColor: borderColor,
borderWidth: '4',
},
{
label: 'Warning',
data: dataPack2,
backgroundColor: "#FFA000",
hoverBackgroundColor: "#FFCA28",
hoverBorderWidth: 0,
borderColor: borderColor,
borderWidth: '4',
},
{
label: 'Error',
data: dataPack3,
backgroundColor: "#D32F2F",
hoverBackgroundColor: "#EF5350",
hoverBorderWidth: 0,
borderColor: borderColor,
borderWidth: '4',
},
]
},
options: {
onClick: function(e){
var element = this.getElementAtEvent(e);
borderColor[element[0]._index] = 'rgb(244,140,4)';
this.update();
},
animation: {
duration: 10,
},
tooltips: {
mode: 'label',
callbacks: {
label: function(tooltipItem, data) {
return data.datasets[tooltipItem.datasetIndex].label + ": " + numberWithCommas(tooltipItem.yLabel);
}
}
},
scales: {
xAxes: [{
stacked: true,
gridLines: { display: false },
}],
yAxes: [{
stacked: true,
ticks: {
callback: function(value) { return numberWithCommas(value); },
},
}],
}, // scales
legend: {display: true}
} // options
}
);

Chart.js bar chart mouse hovering highlights all the datasets instead of just the selected one

I have a very simple (horizontal) bar chart using Chart.js latest version.
https://codepen.io/decho/pen/abZYrxO
It has 3 datasets, Foo, Bar & Baz. The problem is that when I hover with the mouse over a bar (dataset), all 3 of them get highlighted instead of just the one that mouse is over at.
Current behavior:
Expected behavior:
I have tried playing with the tooltip settings, but couldn't quite figure it out. So any idea on how can I achieve this?
Here is my current Chart configuration, also on Codepen:
const ctx = document.getElementById('canvas').getContext('2d');
new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: ['Test'],
datasets: [
{
label: 'Foo',
data: [1],
backgroundColor: 'rgba(23, 83, 139, 0.5)',
borderColor: '#17538b',
borderWidth: 2,
hoverBackgroundColor: "#17538b",
hoverBorderColor: "#4444442b",
},
{
label: 'Bar',
data: [2],
backgroundColor: 'rgba(255, 85, 33, 0.5)',
borderColor: '#ff5521',
borderWidth: 2,
hoverBackgroundColor: "#ff5521",
hoverBorderColor: "#4444442b",
},
{
label: 'Baz',
data: [3],
backgroundColor: 'rgba(62, 211, 241, 0.5)',
borderColor: '#3ed3f1',
borderWidth: 2,
hoverBackgroundColor: "#3ed3f1",
hoverBorderColor: "#4444442b",
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
legend: {
position: 'bottom',
},
tooltips: {
mode: 'dataset',
intersect: true
},
scales: {
xAxes: [
{
display: true,
ticks: {
beginAtZero: true,
},
},
],
yAxes: [{
display: false,
ticks: {
beginAtZero: true,
},
}],
}
},
});
Any help is kindly appreciated.
Solved this myself: Apparently there is a a global option for hover modes:
options: {
hover: {
mode: 'dataset',
}
}
If anyone wonders as me, how this could be done for chart of type: 'line', just add these options:
options: {
elements: {
point: {
hitRadius: 1,
}
},
tooltips: {
mode: 'single',
},
hover: {
mode: 'dataset',
intersect: false
}
}
and also don´t forget to add colors for highlighting (for each dataset obviously):
datasets: [
{
label: "A",
data: [12, 19, 3, 5, 2, 3],
borderWidth: 3,
borderColor: '#ec5e5f',
hoverBorderColor: '#e41a1c',
pointHoverBackgroundColor: "#fff",
lineTension: 0.5,
}
]
For full example check out this codepen

Why is this Chart.js graph not showing the right colors?

I'm using Chart.js to show a bar graph from two datasets for every month to show the evolution of certain data. Now the problem is the way Chart.js is showing these bars.
var myChartIBM = new Chart(ctx1, {
type: 'bar',
plugins: [ChartDataLabels],
data: {
datasets: [{
label: "Número de entregas",
data: entregas,
backgroundColor: [
'rgba(255, 99, 132, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)'
],
borderWidth: 1
},
{
label: "Número de reversas",
data: reversas,
backgroundColor: [
'rgba(54, 162, 235, 0.2)'
],
borderColor: [
'rgba(54, 162, 235, 1)'
],
borderWidth: 1
}
]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
stepSize: 500
}
}],
xAxes: [{
type: 'time',
distribution: 'series',
offset: true,
ticks: {
source: 'data'
},
time: {
unit: 'month',
tooltipFormat: 'll'
}
}]
},
tooltips: {
mode: 'x', // optional
callbacks: {
title: function(tooltipItems, data) {
let tI = tooltipItems[0];
return data.datasets[tI.datasetIndex].data[tI.index].x;
}
}
},
plugins: {
datalabels: {
formatter: function(value, context) {
return value['y'];
}
}
},
responsive: true,
maintainAspectRatio: true
}
});
My doubt is why is it showing grey after the first dataset and what could I do to fix this. Maybe should I always add a zero?
This is the code on PHP side which generates the data:
$col_entregas = Device::select(DB::raw("count(serial) as cantidad, year(fecha_recepcion) as year, month(fecha_recepcion) as month"))->whereNotNull('fecha_recepcion')->whereNotNull('guia_recepcion')->groupBy('year', 'month')->orderBy('year', 'asc')->orderBy('month', 'asc')->get();
$col_reversas = Device::select(DB::raw("count(serial) as cantidad, year(fecha_reversa) as year, month(fecha_reversa) as month"))->whereNotNull('fecha_reversa')->whereNotNull('guia_reversa')->groupBy('year', 'month')->orderBy('year', 'asc')->orderBy('month', 'asc')->get();
$ar_entregas = [];
$ar_reversas = [];
$meses_grafico = [];
foreach($col_entregas as $entrega)
{
$fecha = $entrega->year . '-' . $entrega->month;
$meses_grafico[] = $fecha;
$ar_entregas[] = ["x" => $fecha,
"y" => $entrega->cantidad];
}
foreach($col_reversas as $reversa)
{
$fecha = $reversa->year . '-' . $reversa->month;
$ar_reversas[] = [
"x" => $fecha,
"y" => $reversa->cantidad];
}
So I had to add a foreach to add the same color to every entry in both arrays and that fixed the issue:
var colors_entregas = [];
var border_entregas = [];
entregas.forEach(function (el){
colors_entregas.push('rgba(255, 99, 132, 0.2)');
border_entregas.push('rgba(255, 99, 132, 1)');
});
var colors_reversas = [];
var border_reversas = [];
reversas.forEach(function (el){
colors_reversas.push('rgba(54, 162, 235, 0.2)');
border_reversas.push('rgba(54, 162, 235, 1)');
});
Inside every dataset:
data: entregas,
backgroundColor: colors_entregas,
borderColor: border_entregas,

Categories