Class does not have id chartjs - chart.js

I need a help!
const annotationLinePlugin = {
renderAnnotationLine: function(chartInstance, line) {
let datasetMeta = chartInstance.getDatasetMeta(line.datasetIndex);
let context = chartInstance.chart.ctx;
let datasetModel = datasetMeta.data[line.dataIndex]._model;
const xBarCenter = datasetModel.x
const barWidth = datasetModel.width;
const xStart = xBarCenter - (barWidth / 2) - 4;
const xEnd = xBarCenter + (barWidth / 2) + 4;
const yAxisID = datasetMeta.yAxisID;
const yCoordinate = chartInstance.scales[yAxisID].getPixelForValue(line.yCoordinate);
context.beginPath();
context.strokeStyle = line.color;
context.lineWidth = line.width;
context.moveTo(xStart, yCoordinate);
context.lineTo(xEnd, yCoordinate);
context.stroke();
if (typeof line.label !== typeof undefined) {
context.textAlign = 'center';
context.fillStyle = line.color;
context.fillText(line.label, yCoordinate, xEnd + 7);
}
},
afterDatasetsDraw: function(chart, easing) {
if (chart.data.datasets.length < 1) {
return;
}
if (chart.config.lines) {
chart.config.lines.map(line => {
this.renderAnnotationLine(chart, line);
});
}
}
};
Chart.register(annotationLinePlugin);
I get a error in browser : Uncaught Error : class does not have id
picture with errors
I had chartjs2, i had to migrate to chartjs3.In chartjs2, there were red lines on every chart object, after switching to chaartjs3, these lines were gone, in the record of cases that Chart.plugins.register was replaced by chart.register, I replaced it, but still nothing worked.
There were the lines :
lines
index.html :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Chart.js 2 to 3</title>
</head>
<body>
<div>
<canvas id="сhart"></canvas>
</div>
</body>
<script src="./chartjs3/chart.js"></script>
<script src="./chartjs3/chart.min.js"></script>
<script src="./chartjs3/chartjs-plugin-annotation.js"></script>
<script src="./chartjs3/chartjs-plugin-annotation.min.js"></script>
<script src="./chartjs3/chartjs-plugin-annotationLine.js"></script>
<script src="./chartjs3/chartjs-plugin-datalabels.js"></script>
<script src="./chartjs3/chartjs-plugin-datalabels.min.js"></script>
<script src="./chartjs3/hammer.min.js"></script>
<script>
const ctx = document.getElementById("сhart").getContext("2d");
const options = {
type: "bar",
responsive: true,
maintainAspectRatio: false,
title: { display: false },
tooltips: {
mode: "index",
intersect: true,
filter: (tooltipItem, chartData) => {
return (
!chartData.datasets[tooltipItem.datasetIndex].hideFromTooltip &&
(chartData.datasets[tooltipItem.datasetIndex].yAxisID == "R" ||
tooltipItem.yLabel > 0)
);
},
callbacks: {
label: function (tooltipItem, chartData) {
return (
chartData.datasets[tooltipItem.datasetIndex].label +
": " +
tooltipItem.yLabel.round(0)
);
},
},
},
animation: {
duration: 500,
easing: "linear",
},
plugins: {
datalabels: {
display: function (context) {
const axisMax = context.chart.scales["L"]
? context.chart.scales["L"].max
: 0;
const datasetLabelsSettings =
context.chart.data.datasets[context.datasetIndex].datalabels;
const isLabelOutside =
datasetLabelsSettings &&
datasetLabelsSettings.anchor === "end" &&
datasetLabelsSettings.align === "end";
return (
context.chart.isDatasetVisible(context.datasetIndex) &&
report.chartSettings.showDataLabels &&
((isLabelOutside &&
context.dataset.data[context.dataIndex] > 0) ||
context.dataset.data[context.dataIndex] >
axisMax * HIDE_DATALABELS_Y_LEVEL) &&
context.dataset.type !== "line"
);
},
formatter: (x) => {
return (x || 0).round(0);
},
},
},
legend: {
display: true,
onClick: function (event, legendItem) {
var index = legendItem.datasetIndex,
meta = null;
var tag = chart.data.datasets[index].tag;
var meta = chart.getDatasetMeta(index);
if (tag || isNumeric(tag)) {
chart.data.datasets
.filter((x) => x.tag == tag)
.forEach((item) => {
item.hidden = item.forcedHidden || !item.hidden;
});
} else {
meta.hidden = meta.hidden === null ? !meta.hidden : null;
}
chart.update();
},
position: "top",
labels: {
usePointStyle: true,
filter: function (legendItem, chartData) {
return (
chartData.datasets[legendItem.datasetIndex].displayInLegend ||
chartData.datasets[legendItem.datasetIndex].displayInLegend ==
undefined
);
return false;
},
},
},
cales: {
xAxes: [
{
stacked: true,
scaleLabel: {
display: true,
labelString: "Период",
},
},
],
yAxes: [
{
id: "L",
type: "linear",
position: "left",
stacked: true,
scaleLabel: {
display: true,
labelString: "Трудозатраты, чел-мес",
},
},
],
},
lines: [
5553, 5454, 5399, 5290, 5260, 4927, 4565, 4649, 5324, 5624, 5710, 5766,
5827, 5829, 5831, 5832, 5833, 5835, 5835, 5836, 5836, 5836, 5837, 583,
].map((x, i) => {
return {
datasetIndex: 0,
dataIndex: i,
yCoordinate: x,
color: "salmon",
width: 3,
};
}),
data: {
labels: [
"2022-01",
"2022-02",
"2022-03",
"2022-04",
"2022-05",
"2022-06",
"2022-07",
"2022-08",
"2022-09",
"2022-10",
"2022-11",
"2022-12",
"2023-01",
"2023-02",
"2023-03",
"2023-04",
"2024-05",
"2023-06",
"2023-07",
"2023-08",
"2023-09",
"2023-10",
"2023-11",
"2023-12",
],
datasets: [
{
label: "ОПИ (ресурсный профиль)",
findId: "ОПИ",
tag: "ОПИ",
type: "bar",
borderWidth: 1,
stack: "cnt",
groupType: "cnt",
backgroundColor: "#2196f3",
fill: false,
pointStyle: "rect",
detailMode: "point",
data: [
5356, 5802, 6105, 6258, 6524, 6690, 6697, 7348, 6578, 5742, 4626,
3678, 4809, 6038, 5891, 4526, 3637, 3601, 3471, 3618, 3548, 3428,
3330, 3448, 5031,
],
displayInLegend: false,
datalabels: {
anchor: "end",
align: "end",
offset: 0,
color: function (context) {
return "#2196f3";
},
},
},
],
},
};
const сhart = new Chart(ctx, options);
</script>
</html>

The plugin needs to have an unique id.
You could add an id (see following example):
const annotationLinePlugin = {
id: 'annotationLine',
....
}
Be also aware the afterDatasetsDraw hook has got a different signature (arguments).
https://www.chartjs.org/docs/latest/api/interfaces/Plugin.html#afterdatasetsdraw

Related

Chart.min.js - sometimes the chart is displayed and sometimes not

hello I have the following problem I use the plugin Chart.js to display statistics now I have the following problem sometimes the chart is displayed and sometimes not see screenshot
I have asked in a Discord Chat but that did not help, i hope i can get help here
Chart.js Version:2.1.4
The Code:
function LoadProviderChart() {
$(document).ready(function() {
var providercount = JSON.parse(window.localStorage.getItem('providercount_' + window.location.pathname.split('/')[4]));
console.log(providercount);
providercount = Object.keys(providercount).map(function (key) {
return [key, providercount[key]];
})
providercount.sort(function (a, b) {
return b[1].count - a[1].count;
})
var cutProviderCount = providercount.slice(0, 6);
if(window.ProviderChart != null) {
window.ProviderChart.destroy();
console.log("ProviderChart destroyed");
}
Chart.defaults.global.defaultFontFamily = 'Rubik';
Chart.defaults.global.defaultFontColor = '#fff';
var ctxB = document.getElementById("barChart").getContext('2d');
var config = {
type: 'bar',
data: {
labels: cutProviderCount.map(function(x) { return x[0] }),
datasets: [{
label: 'Games of Providers',
data: cutProviderCount.map(function(x) { return x[1].count }),
backgroundColor: "" + getComputedStyle(document.body).getPropertyValue('--highlight_color').toString().replace(' ', '') + "",
}],
},
options: {
scales: {
yAxes: [{
gridLines:{
display: false,
},
ticks: {
beginAtZero: true
}
}],
xAxes: [{
gridLines:{
display:false,
},
}]
}
}
}
window.ProviderChart = new Chart(ctxB, config);
});
}

Chartjs v. 3.7.1 Zoom out not working after zooming in too much

I am realtively new in front end and Chartjs. My problem is when I zoom in more than a certain point I can not zoom out back. I tried mouse wheel option and added a "zoom out" button but both not working. When I zoom in just a little I dont face the same problem. I tried on Chrome and Edge browsers, both has the same behavior.
CahartJs version: 3.7.1
chartjs-plugin-zoom version: 1.2.0
Note: Second graph is for data decimation which is irrelevant with current problem.
script.js and index.html:
//data creation
let labels2 = [];
let data2 = []
i = 0
k = 0
while (k < 250) {
if (i < 10) {
let last_hall = 600
data2.push(last_hall)
labels2.push(k);
i++
}
if (i >= 10) {
let last_hall = 500
data2.push(last_hall)
labels2.push(k);
i++
if (i == 20) {
for (let j = 0; j < 30; j++) {
let last_hall = getRandomInt(800, 1300)
data2.push(last_hall)
labels2.push(k);
k++
}
i = 0
}
}
k++
}
//data2 = data2.fill(0).map(() => Math.random());
// let labels2 = [];
// let i = 0;
// data2.forEach(element => {
// labels2.push(i);
// i++;
// });
let data3 = [];
data3 = data2;
let labels3 = [];
labels3 = labels2;
ColorArr = ["red", "blue", "red"]
let backgroundColorArr = [];
let backgroundColorArr2 = [];
var ctx = document.getElementById('myChart2').getContext('2d');
var ctx2 = document.getElementById('myChart3').getContext('2d');
const up = (ctx, value) => ctx.p0.parsed.y >= ctx.p1.parsed.y ? value : undefined;
const down = (ctx, value) => ctx.p0.parsed.y < ctx.p1.parsed.y ? value : undefined;
const up2 = (ctx2, value) => ctx2.p0.parsed.y >= ctx2.p1.parsed.y ? value : undefined;
const down2 = (ctx2, value) => ctx2.p0.parsed.y < ctx2.p1.parsed.y ? value : undefined;
data2.forEach(element => {
backgroundColorArr.push(ColorArr[i % 3]);
i++;
});
backgroundColorArr2 = backgroundColorArr;
//Decimation
// if (data3.length > 1000) {
// var datastep = Math.floor(data3.length / 1000) //always show 1000 values from all data
// console.log(datastep);
// for (i = 0; i < 1000; i++) {
// data3[i] = data3[i * datastep];
// backgroundColorArr2[i] = backgroundColorArr2[i * datastep];
// labels3[i] = labels3[i * datastep];
// }
// }
// console.log(labels3)
// data3 = data3.slice(0, 1000);
// labels3 = labels3.slice(0, 1000);
// backgroundColorArr2 = backgroundColorArr2.slice(0, 1000);
let chart3 = new Chart(ctx, {
type: 'line',
data: {
labels: labels2,
datasets: [{
data: data2,
fill: true,
//backgroundColor: backgroundColorArr[ctx.p0DataIndex],
segment: {
borderColor: ctx => up(ctx, 'rgba(75,192,192,1)') || down(ctx, "red"),
backgroundColor: ctx => up(ctx, 'rgba(75,192,192,1)') || down(ctx, "red"), //backgroundColorArr[ctx.p0DataIndex]
},
}]
},
options: {
//parsing: false,
scales: {
x: {
ticks: {
maxTicksLimit: 10,
autoSkip: true,
}
},
y: {
beginAtZero: true
}
},
datasets: {
parsing: false,
line: {
pointRadius: 0 // disable for all `'line'` datasets
}
},
plugins: {
// decimation: {
// algorithm: 'lttb',
// enabled: true,
// samples: 20
// },
zoom: {
pan: {
enabled: true,
mode: 'x',
},
zoom: {
wheel: {
enabled: true,
},
pinch: {
enabled: true
},
mode: 'x',
}
}
}
}
});
let chart2 = new Chart(ctx2, {
type: 'line',
data: {
labels: labels3,
datasets: [{
data: data3,
fill: true,
//backgroundColor: backgroundColorArr[ctx.p0DataIndex],
segment: {
borderColor: ctx2 => up2(ctx2, 'rgba(75,192,192,1)') || down(ctx2, "red"),
backgroundColor: ctx2 => up2(ctx2, 'rgba(75,192,192,1)') || down(ctx2, "red"), //backgroundColorArr[ctx.p0DataIndex],
},
}]
},
options: {
//parsing: false,
scales: {
x: {
ticks: {
maxTicksLimit: 10,
autoSkip: true,
}
},
y: {
beginAtZero: true
}
},
datasets: {
parsing: false,
line: {
pointRadius: 0 // disable for all `'line'` datasets
}
},
plugins: {
// decimation: {
// algorithm: 'lttb',
// enabled: true,
// samples: 20
// },
zoom: {
pan: {
enabled: true,
mode: 'x',
},
zoom: {
//enabled: true,
wheel: {
enabled: true,
},
pinch: {
enabled: true
},
mode: 'x',
}
}
}
}
});
function resetZoom() {
chart2.resetZoom();
}
function resetZoom2() {
chart3.resetZoom();
}
function zoomButton() {
chart2.zoom(1.1);
}
function zoomButton2() {
chart3.zoom(1.3);
}
function zoomOutButton() {
chart2.zoom(0.9);
}
function zoomOutButton2() {
chart3.zoom(0.7);
}
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min); //The maximum is exclusive and the minimum is inclusive
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Charts, Charts, Charts</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="mystyle.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.7.1"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"
integrity="sha512-UXumZrZNiOwnTcZSHLOfcTs0aos2MzBWHXOHOuB0J/R44QB0dwY5JgfbvljXcklVf65Gc4El6RjZ+lnwd2az2g=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/1.2.0/chartjs-plugin-zoom.min.js"
integrity="sha512-TT0wAMqqtjXVzpc48sI0G84rBP+oTkBZPgeRYIOVRGUdwJsyS3WPipsNh///ay2LJ+onCM23tipnz6EvEy2/UA=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<button onclick="download_csv_file()"> Download CSV </button>
<h1>Charts, Charts, Charts</h1>
<div class="container">
<div class="row">
<div class="col-6 chart">
<canvas id="myChart2" width="1000" height="800"></canvas>
<button onclick="resetZoom2()"> Reset </button>
<button onclick="zoomOutButton2()"> Zoom Out </button>
<button onclick="zoomButton2()"> Zoom In</button>
</div>
</div>
<div class="row">
<div class="col-6 chart">
<canvas id="myChart3" width="1000" height="800"></canvas>
<button onclick="resetZoom()"> Reset </button>
<button onclick="zoomButton()"> Zoom In</button>
<button onclick="zoomOutButton()"> Zoom Out</button>
</div>
<div class="col-6 chart">
<canvas id="myChart4" width="500" height="400"></canvas>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Just had the same problem.
https://github.com/chartjs/chartjs-plugin-zoom/issues/696
Possible solution:
Don't use numbers as labels, use String versions of them
by using .toString() on items of the array.
[1, 2, 3] -> ["1", "2", "3"]

Chart.js customization, two Y-Axis overlay, chart area padding, odd tick padding

The Chart.js plugin is super, excellent, beautiful and easily customizable.
But even all this did not help me solve a few problems.
I have to create pixel perfect chart according to design shown on picture 1
I hope for your help!!
How to make those lines that without signatures be longer than those with signatures
I did the indentation from the scales to the graphs using the tickMarkLength parameter, but maybe it’s possible somehow under another, because you can see the overlap of one scale on another.
How to make the grid lines of the left and right scales coincide?
I set beforeUpdate .stepSize, but in spite of the fact that I specify that there should be 8 intervals, sometimes 8, then 9.
There is a link to my current code:
function data_generation(values_obj) {
let max_val=-900;
let min_val=0;
Object.keys(values_obj).forEach(function(key) {
chart_object={};
chart_object.label= values_obj[key].name;
chart_object.data= Object.values(values_obj[key].data);
chart_object.backgroundColor= values_obj[key].color;
if(key == 'TempOutdoor') {
chart_object.yAxisID = 'right-y-axis';
chart_object.backgroundColor= "transparent";
chart_object.pointRadius= 4;
chart_object.lineTension= 0;
chart_object.pointBackgroundColor="#FFF";
chart_object.pointBorderColor= "#60AD5E";
chart_object.borderColor= "#60AD5E";
chart_object.pointBorderWidth= 2;
chart_object.type= 'line';
} else {
chart_object.yAxisID = 'left-y-axis';
chart_object.lineTension= 0;
}
config.data.datasets.push(chart_object);
//find common min and max values
//min
if(min_val>parseFloat(values_obj[key].min)) {
min_val = parseFloat(values_obj[key].min);
}
//max
if(max_val < parseFloat(values_obj[key].max)) {
max_val = parseFloat(values_obj[key].max);
}
});
}
var config = {
drawTicks:false,
type: 'bar',
data: {
datasets: [ ],
labels: ''
},
options: {
animation: {
duration: 0
},
'legend':false,
responsive:true,
maintainAspectRatio: false,
scales: {
xAxes: [{
stacked: true,
barThickness: ($(window).width()<991.99)?14:24,
ticks: {
fontSize: ($(window).width()<991.99)?10:14,
},
gridLines : {
display : false
}
}],
yAxes: [
{
beforeUpdate: function(scale) {
//1 find max and min through all leftlabels
var left_side_list = config.data.datasets.filter(obj => {return obj.yAxisID == "left-y-axis"});
var left_side_list_data = [].concat(...Object.keys(left_side_list).map(e => left_side_list[e].data));
let max_val = Math.max.apply(Math,left_side_list_data);
let min_val = Math.min.apply(Math,left_side_list_data);
// 8 intervals - 9 lines
let left_iterval = (max_val - min_val) / 8;
//set stepsize
scale.chart.options.scales.yAxes[0].ticks.stepSize = left_iterval;
return;
},
id: 'left-y-axis',
type: 'linear',
position: 'left',
ticks: {
beginAtZero: false,
fontSize: ($(window).width()<991.99)?10:14,
callback: function(value, index, values) {
if(index % 2 == 0 || index==0) {
return ' ';
} else {
return " "+value.toFixed(0)+" ";
}
}
},
gridLines: {
drawBorder: false,
tickMarkLength: ($(window).width()<991.99)?34:84,
}
},
{
beforeUpdate: function(scale) {
//var nMaxRev = Math.max.apply(Math,scale.chart.config.data.datasets[1].data);
//get right object data
var temp_list = config.data.datasets.filter(obj => {return obj.yAxisID == "right-y-axis"});
//var temp_list = scale.chart.config.data.datasets.filter(obj => {return obj.yAxisID == "right-y-axis"});
//console.log(temp_list);
if(temp_list[0].data !== undefined || temp_list[0].data != []) {
var nMaxRev = Math.max.apply(Math, temp_list[0].data);
var nMinRev = Math.min.apply(Math, temp_list[0].data);
var nLeftTickCount = 8;
if(nMinRev<0) {
nLeftTickCount = 7;
}
var nTickInterval = (nMaxRev - nMinRev) / nLeftTickCount;
scale.chart.options.scales.yAxes[1].ticks.stepSize = nTickInterval;
}
return;
},
id: 'right-y-axis',
type: 'linear',
position: 'right',
ticks: {
beginAtZero: false,
fontSize: ($(window).width()<991.99)?10:14,
callback: function(value, index, values) {
if(index % 2 == 0 || index==0) {
return '';
} else {
return " "+value.toFixed(0);
}
}
},
gridLines: {
drawBorder: false,
tickMarkLength: ($(window).width()<991.99)?34:84,
}
}
]
}
}
};
window.onload = function() {
var data_string='{"success":true,"axis":["Пн","Вт","Ср","Чт","Пт","Сб","Вс"],"data":{"TempOutdoor":{"period_start":"2021-05-10 00:00:00","period_end":"2021-05-16 23:59:59","data":{"Пн":-4.9787234042553195,"Вт":-2.9166666666666665,"Ср":-3.3125,"Чт":2.5208333333333335,"Пт":6.84375,"Сб":0,"Вс":0},"min":"-4.98","max":"6.84","avg":"-0.26","sum":"-1.84","name":"Температура на улице","color":"#60AD5E","value_type":"instant"},"MotoHW":{"period_start":"2021-05-10 00:00:00","period_end":"2021-05-16 23:59:59","data":{"Пн":11,"Вт":15,"Ср":13,"Чт":12,"Пт":9,"Сб":0,"Вс":0},"min":"0.00","max":"15.00","avg":"8.57","sum":"60.00","name":"Мотогодини: Гаряча вода","color":"#29819D","value_type":"counter"}},"closestPeriods":{"previous":{"2021-05-03 00:00:00":"03.05 - 09.05"},"current":{"2021-05-10 00:00:00":"10.05 - 16.05"},"next":null}}';
var data = JSON.parse(data_string);
config.data.labels = data.axis;
var values_obj = data.data;
data_generation(values_obj);
//console.log(JSON.stringify(config));
var ctx = document.getElementById('StatisticsChartCanvas').getContext('2d');
window.StatisticsChart = new Chart(ctx,config);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="chart-wrapper" style="width:548px; height:265px;">
<canvas id="StatisticsChartCanvas"></canvas>
</div>
After a huge amount of ideas, attempts and errors, I found a solution for the full customization of the scale.
For this I
Disabled the display of gridlines and ticks using property
display:false,
Wrote a plugin that draw gridlines and ticks in accordance with the design.
That's what happened
var AikGridLinePlugin = {
beforeDraw: function(chartInstance) {
var yScaleLeft = chartInstance.scales["left-y-axis"];
var yScaleRight = chartInstance.scales["right-y-axis"];
var canvas = chartInstance.chart;
var ctx = canvas.ctx;
//left axis
var left_side_list = chartInstance.data.datasets.filter(obj => {return obj.yAxisID == "left-y-axis"});
var left_side_list_data = [].concat(...Object.keys(left_side_list).map(e => left_side_list[e].data));
let left_side_list_max = Math.max.apply(Math,left_side_list_data);
let left_side_list_min = Math.min.apply(Math,left_side_list_data);
let left_iterval = (left_side_list_max - left_side_list_min) / 8;
// right axis
var right_side_list = chartInstance.data.datasets.filter(obj => {return obj.yAxisID == "right-y-axis"});
var right_side_list_data = [].concat(...Object.keys(right_side_list).map(e => right_side_list[e].data));
let right_side_list_max = Math.max.apply(Math,right_side_list_data);
let right_side_list_min = Math.min.apply(Math,right_side_list_data);
let right_iterval = (right_side_list_max - right_side_list_min) / 8;
var current_value_left = left_side_list_min,
current_value_right = right_side_list_min,
current_value_right_text=0;
for(var i=1;i<10;i++) {
ctx.lineWidth = 1;
ctx.font = "13px Roboto";
ctx.fillStyle = "#666666";
ctx.beginPath();
if(i%2==0) {
ctx.moveTo(47, yScaleLeft.getPixelForValue(current_value_left));
ctx.lineTo((canvas.width-47), yScaleLeft.getPixelForValue(current_value_left));
ctx.fillText((current_value_left>1)?current_value_left.toFixed(0):current_value_left.toFixed(1), 5, yScaleLeft.getPixelForValue(current_value_left)+5);
current_value_right_text=(current_value_right>1 || current_value_right<-1)?current_value_right.toFixed(0):current_value_right.toFixed(1);
ctx.fillText(current_value_right_text, (canvas.width-5-ctx.measureText(current_value_right_text).width), yScaleLeft.getPixelForValue(current_value_left)+5);
} else {
ctx.moveTo(15, yScaleLeft.getPixelForValue(current_value_left));
ctx.lineTo((canvas.width-15), yScaleLeft.getPixelForValue(current_value_left));
}
ctx.strokeStyle = "#91979F";
ctx.stroke();
current_value_left = current_value_left+left_iterval;
current_value_right = current_value_right+right_iterval;
}
return;
}
};
Chart.pluginService.register(AikGridLinePlugin);
function data_generation(values_obj) {
let max_val=-900;
let min_val=0;
Object.keys(values_obj).forEach(function(key) {
chart_object={};
chart_object.label= values_obj[key].name;
chart_object.data= Object.values(values_obj[key].data);
chart_object.backgroundColor= values_obj[key].color;
if(key == 'TempOutdoor') {
chart_object.yAxisID = 'right-y-axis';
chart_object.backgroundColor= "transparent";
chart_object.pointRadius= 4;
chart_object.lineTension= 0;
chart_object.pointBackgroundColor="#FFF";
chart_object.pointBorderColor= "#60AD5E";
chart_object.borderColor= "#60AD5E";
chart_object.pointBorderWidth= 2;
chart_object.type= 'line';
} else {
chart_object.yAxisID = 'left-y-axis';
chart_object.lineTension= 0;
}
config.data.datasets.push(chart_object);
//find common min and max values
//min
if(min_val>parseFloat(values_obj[key].min)) {
min_val = parseFloat(values_obj[key].min);
}
//max
if(max_val < parseFloat(values_obj[key].max)) {
max_val = parseFloat(values_obj[key].max);
}
});
}
var config = {
drawTicks:false,
type: 'bar',
data: {
datasets: [ ],
labels: ''
},
options: {
animation: {
duration: 0
},
'legend':false,
responsive:true,
maintainAspectRatio: false,
scales: {
xAxes: [{
id: 'x-axis',
stacked: true,
barThickness: ($(window).width()<991.99)?14:24,
ticks: {
fontSize: ($(window).width()<991.99)?10:14,
},
gridLines : {
display : false
}
}],
yAxes: [
{
id: 'left-y-axis',
type: 'linear',
position: 'left',
ticks: {
display:false,
},
gridLines: {
display : false,
drawBorder: false,
tickMarkLength: ($(window).width()<991.99)?34:84,
}
},
{
id: 'right-y-axis',
type: 'linear',
position: 'right',
ticks: {
display : false,
beginAtZero: false,
fontSize: ($(window).width()<991.99)?10:14,
},
gridLines: {
display : false,
drawBorder: false,
tickMarkLength: ($(window).width()<991.99)?34:84,
}
}
]
}
}
};
window.onload = function() {
var data_string='{"success":true,"axis":["Пн","Вт","Ср","Чт","Пт","Сб","Вс"],"data":{"TempOutdoor":{"period_start":"2021-05-10 00:00:00","period_end":"2021-05-16 23:59:59","data":{"Пн":-4.9787234042553195,"Вт":-2.9166666666666665,"Ср":-3.3125,"Чт":2.5208333333333335,"Пт":6.84375,"Сб":0,"Вс":0},"min":"-4.98","max":"6.84","avg":"-0.26","sum":"-1.84","name":"Температура на улице","color":"#60AD5E","value_type":"instant"},"MotoHW":{"period_start":"2021-05-10 00:00:00","period_end":"2021-05-16 23:59:59","data":{"Пн":11,"Вт":15,"Ср":13,"Чт":12,"Пт":9,"Сб":0,"Вс":0},"min":"0.00","max":"15.00","avg":"8.57","sum":"60.00","name":"Мотогодини: Гаряча вода","color":"#29819D","value_type":"counter"}},"closestPeriods":{"previous":{"2021-05-03 00:00:00":"03.05 - 09.05"},"current":{"2021-05-10 00:00:00":"10.05 - 16.05"},"next":null}}';
var data = JSON.parse(data_string);
config.data.labels = data.axis;
var values_obj = data.data;
data_generation(values_obj);
//console.log(JSON.stringify(config));
var ctx = document.getElementById('StatisticsChartCanvas').getContext('2d');
window.StatisticsChart = new Chart(ctx,config);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="chart-wrapper" style="width:548px; height:265px;">
<canvas id="StatisticsChartCanvas"></canvas>
</div>

How to hide Chart.js data labels for small screens

I am trying to hide data labels generated by the data labels plugin for small screens.
I thought that I could use the onResize property of chartjs and set display to false when the width got small. This is much like the hide labels solution found here.
Unfortunately, I've not been able to get this to work. I have the following CodePen that doesn't work.
var moneyFormat = wNumb({
decimals: 0,
thousand: ',',
prefix: '$',
negativeBefore: '-'
});
var percentFormat = wNumb({
decimals: 0,
suffix: '%',
negativeBefore: '-'
});
/*
* Unregister chartjs-plugins-datalabels - not really necessary for this use case
*/
Chart.plugins.unregister(ChartDataLabels);
var doughnutdata = {
labels: ['Housing',
'Food',
'Transportation',
'Clothing',
'Healthcare',
'Childcare',
'Misc'],
datasets: [
{
backgroundColor: [
'#9B2A00',
'#5B5C90',
'#6B8294',
'#1A6300',
'#BE0000',
'#B8A853',
'#64A856'
],
borderColor: [
'#FFFFFF',
'#FFFFFF',
'#FFFFFF',
'#FFFFFF',
'#FFFFFF',
'#FFFFFF',
'#FFFFFF'
],
data: [88480, 57680, 40050, 18430, 23860, 25840, 17490]
}
]
};
var chartOptions = {
responsive: true,
maintainAspectRatio: true,
legend: {
labels: {
boxWidth: 20
}
},
tooltips: {
callbacks: {
label: function (tooltipItem, data) {
var index = tooltipItem.index;
return data.labels[index] + ': ' + moneyFormat.to(data.datasets[0].data[index]) + '';
}
}
},
plugins: {
datalabels: {
anchor: 'end',
backgroundColor: function (context) {
return context.dataset.backgroundColor;
},
borderColor: 'white',
borderRadius: 25,
borderWidth: 1,
color: 'white',
font: {
size: 10
},
formatter: function (value, pieID) {
var sum = 0;
var dataArr = pieID.chart.data.datasets[0].data;
dataArr.map(function (data) {
sum += data;
});
var percentage = percentFormat.to((value * 100 / sum));
return percentage;
}
}
}
};
var doughnutID = document.getElementById('doughnutchart').getContext('2d');
var pieChart = new Chart(doughnutID, {
plugins: [ChartDataLabels],
type: 'doughnut',
data: doughnutdata,
options: chartOptions,
onResize: function(chart, size) {
var showLabels = (size.width < 500) ? false : true;
chart.options = {
plugins: {
datalabels: {
display: showLabels
}
}
};
}
});
Any ideas concerning what I'm doing wrong (and fixes) would be greatly appreciated.
Responsiveness can be implemented using scriptable options and in your case, you would use a function for the display option that returns false if the chart is smaller than a specific size. (Example):
options: {
plugins: {
datalabels: {
display: function(context) {
return context.chart.width > 500;
}
}
}
}
As usual, as soon as I post a question I come up with an answer. One solution using inline plugin definitions is given at the following CodePen. If you put a browser into developer mode and shrink the window to less than 540 px, the data labels will vanish.
The code is shown below:
"use strict";
/* global Chart */
/* global wNumb */
/* global ChartDataLabels */
/*
* Unregister chartjs-plugins-datalabels - not really necessary for this use case
*/
Chart.plugins.unregister(ChartDataLabels);
var moneyFormat = wNumb({
decimals: 0,
thousand: ",",
prefix: "$",
negativeBefore: "-"
});
var percentFormat = wNumb({
decimals: 0,
suffix: "%",
negativeBefore: "-"
});
var doughnutdata = {
labels: [
"Housing",
"Food",
"Transportation",
"Clothing",
"Healthcare",
"Childcare",
"Misc"
],
datasets: [
{
backgroundColor: [
"#9B2A00",
"#5B5C90",
"#6B8294",
"#1A6300",
"#BE0000",
"#B8A853",
"#64A856"
],
borderColor: [
"#FFFFFF",
"#FFFFFF",
"#FFFFFF",
"#FFFFFF",
"#FFFFFF",
"#FFFFFF",
"#FFFFFF"
],
data: [88480, 57680, 40050, 18430, 23860, 25840, 17490]
}
]
};
var chartOptions = {
responsive: true,
maintainAspectRatio: true,
legend: {
labels: {
boxWidth: 20
}
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var index = tooltipItem.index;
return (
data.labels[index] +
": " +
moneyFormat.to(data.datasets[0].data[index]) +
""
);
}
}
},
plugins: {
datalabels: {
anchor: "end",
backgroundColor: function(context) {
return context.dataset.backgroundColor;
},
borderColor: "white",
borderRadius: 25,
borderWidth: 1,
color: "white",
font: {
size: 10
},
formatter: function(value, pieID) {
var sum = 0;
var dataArr = pieID.chart.data.datasets[0].data;
dataArr.map(function(data) {
sum += data;
});
var percentage = percentFormat.to(value * 100 / sum);
return percentage;
}
}
}
};
var doughnutID = document.getElementById("doughnutchart").getContext("2d");
var pieChart = new Chart(doughnutID, {
plugins: [
ChartDataLabels,
{
beforeLayout: function(chart) {
var showLabels = (chart.width) > 500 ? true : false;
chart.options.plugins.datalabels.display = showLabels;
}
},
{
onresize: function(chart) {
var showLabels = (chart.width) > 500 ? true : false;
chart.options.plugins.datalabels.display = showLabels;
}
}
],
type: "doughnut",
data: doughnutdata,
options: chartOptions
});
I hope that this is useful.

Chartjs v2 - format tooltip for multiple data sets (bar and line)

Reading up a lot on how to format the tooltip in ChartJS v2.x utilizing the tooltip callback. I've had success thus far, but find that I'm unable to define two separate formats for the two data sets I have.
As a bit more context, I have a line chart overlayed on top of a bar chart:
My bar data is numerical (in the millions, and needs to be rounded and truncated).
Example: 22345343 needs to be shown as 22M in the tooltip
My line data is a currency
Example: 146.36534 needs to shown as $146.37 in the tooptip
Here's my short WIP code thus far. This formats the tooltip to round and include the $ sign. How can I expand this so that I can separately format my bar data correctly in the tooltip?
tooltips: {
mode: 'index',
intersect: false,
callbacks: {
label: function(tooltipItem, data) {
return "$" + Number(tooltipItem.yLabel).toFixed(2).replace(/./g, function(c, i, a) {
return i > 0 && c !== "." && (a.length - i) % 3 === 0 ? "," + c : c;
});
}
}
}
You could achieve that using the following tooltips callback function ...
callbacks: {
label: function (t, d) {
if (t.datasetIndex === 0) {
return '$' + t.yLabel.toFixed(2)
} else if (t.datasetIndex === 1) {
return Math.round(+t.yLabel.toString().replace(/(\d{2})(.*)/, '$1.$2')) + 'M';
}
}
}
ᴅᴇᴍᴏ
var ctx = document.getElementById("canvas").getContext("2d");
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["January", "February", "March", "April", "May"],
datasets: [{
type: 'line',
label: "Sales",
data: [144.36534, 146.42534, 145.23534, 147.19534, 145],
fill: false,
borderColor: '#EC932F',
backgroundColor: '#EC932F',
tension: 0,
yAxisID: 'y-axis-2'
}, {
type: 'bar',
label: "Visitor",
data: [22345343, 23345343, 24345343, 25345343, 230245343],
backgroundColor: '#71B37C',
yAxisID: 'y-axis-1'
}]
},
options: {
responsive: false,
tooltips: {
mode: 'index',
intersect: false,
callbacks: {
label: function (t, d) {
if (t.datasetIndex === 0) {
return '$' + t.yLabel.toFixed(2);
} else if (t.datasetIndex === 1) {
if (t.yLabel.toString().length === 9) {
return Math.round(+t.yLabel.toString().replace(/(\d{3})(.*)/, '$1.$2')) + 'M';
} else return Math.round(+t.yLabel.toString().replace(/(\d{2})(.*)/, '$1.$2')) + 'M';
}
}
}
},
scales: {
yAxes: [{
id: "y-axis-1",
position: "left"
}, {
id: "y-axis-2",
position: "right"
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<canvas id="canvas" width="400" height="190"></canvas>
Try using below code!
let DoughnutForSavingCount = {
labels: [
intl.formatMessage({ id: 'Guarantee' }),
intl.formatMessage({ id: 'ILAS' }),
intl.formatMessage({ id: 'No Idea' })
],
datasets: [
/* Outer doughnut data starts*/
{
label: 'Graph1',
data: [
_.get(getClientSavingILASPolicyData[0], 'countwithGuaranttee') >
0 &&
_.get(getClientSavingILASPolicyData[0], 'totalWithGuarantee') ===
0
? 0.1
: _.get(getClientSavingILASPolicyData[0], 'totalWithGuarantee'),
_.get(getClientSavingILASPolicyData[0], 'countwithILAS', 0) > 0 &&
_.get(getClientSavingILASPolicyData[0], 'totalWithILAS') === 0
? 0.1
: _.get(getClientSavingILASPolicyData[0], 'totalWithILAS'),
_.get(getClientSavingILASPolicyData[0], 'countNoIdea', 0) > 0 &&
_.get(getClientSavingILASPolicyData[0], 'totalWithNoIdea') === 0
? 0.1
: _.get(getClientSavingILASPolicyData[0], 'totalWithNoIdea')
],
backgroundColor: ['#8c1aff', '#BF80FF', '#E9e9e9'],
hoverBackgroundColor: ['#8c1aff', '#BF80FF', '#E9e9e9']
},
/* Outer doughnut data ends*/
/* Inner doughnut data starts*/
{
label: 'Graph2',
data: [
_.get(getClientSavingILASPolicyData[0], 'countwithGuaranttee'),
_.get(getClientSavingILASPolicyData[0], 'countwithILAS'),
_.get(getClientSavingILASPolicyData[0], 'countNoIdea')
],
backgroundColor: ['#8c1aff', '#BF80FF', '#E9e9e9'],
hoverBackgroundColor: ['#8c1aff', '#BF80FF', '#E9e9e9']
}
/* Inner doughnut data ends*/
],
borderWidth: [1]
};
let DoughnutForSavingCountConfig = {
cutoutPercentage: 70,
legend: {
display: true,
position: 'bottom',
labels: {
fontColor: '#34A0DC',
fontSize: 10,
fontFamily: 'Helvetica',
boxWidth: 10,
usePointStyle: true
}
},
responsive: true,
plugins: {
datalabels: {
display: false
}
},
tooltips: {
enabled: true,
//*****add for reference********** */
callbacks: {
label: function(tooltipItems, data) {
if (tooltipItems.datasetIndex) {
var label = data.labels[tooltipItems.index] || '';
var currentValue =
data.datasets[tooltipItems.datasetIndex].data[
tooltipItems.index
];
if (label) {
label += ': ';
}
label += currentValue == '0.1' ? '0' : currentValue;
return label;
} else {
var label = data.labels[tooltipItems.index] || '';
var currentValue =
data.datasets[tooltipItems.datasetIndex].data[
tooltipItems.index
];
if (label) {
label += ': ';
}
label += intl.formatMessage({ id: 'HKD' }) + ' ';
label +=
currentValue == '0.1'
? '0'
: currentValue
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return label;
}
}
}
}
};
Thanks, GRUNT!
But since my datasets could me mixed it's better to use the yAxisID to check for the correct dataset.
tooltips: {
callbacks: {
label: function (tooltipItem, details) {
if (details.datasets[tooltipItem.datasetIndex].yAxisID == "$") {
let dataset = details.datasets[tooltipItem.datasetIndex];
let currentValue = dataset.data[tooltipItem.index];
return dataset.label + ": " + currentValue.toFixed(2) + " $";
} else {
let dataset = details.datasets[tooltipItem.datasetIndex];
let currentValue = dataset.data[tooltipItem.index];
return dataset.label + ": " + currentValue +" pieces";
}
}
}
}