I'm trying to build a chart.js Line and with scatter points and I would like to have the datalabel to show just on the scatter points, I've tried to do it with datalabels plugin from chart js but it shows the datalabel for all datasets including the line, How can I show just the specific scatter points? this is what the code looks like:
Chart.register(ChartDataLabels);
fetch('https://raw.githubusercontent.com/ConeDigital/assets/main/data.json')
.then(response => response.json())
.then(data => {
var ctx = document.getElementById('myChart2').getContext('2d'),
chart = new Chart(ctx, {
type: 'line',
data: getChartData(data),
options: {
legend: {
display: false
},
plugins: {
datalabels: { // This code is used to display data values
anchor: 'end',
align: 'top',
font: {
weight: 'bold',
size: 16
}
}
},
tooltips: {
mode: 'index',
intersect: false,
callbacks: {
label: function (tooltipItem, data) {
return data.datasets[tooltipItem.datasetIndex].label + ': ' + tooltipItem.yLabel+"%";
},
labelColor: function (tooltipItem, chart) {
let border = ''
let background = ''
let colors
if ( tooltipItem.datasetIndex === 1 ) {
colors = {
borderColor: '#fff',
backgroundColor: '#001F5B'
}
} else {
colors = {
borderColor: '#fff',
backgroundColor: '#eb8484'
}
}
return colors
}
}
},
responsive: true,
scales: {
xAxes: [{
stacked: false,
scaleLabel: {
display: false,
labelString: 'Datum'
},
ticks: {
display: false
},
gridLines: {
display: false,
drawBorder: false,
}
}],
yAxes: [{
stacked: false,
ticks: {
display: false
},
gridLines: {
display: false,
drawBorder: false
},
scaleLabel: {
display: false,
labelString: 'Procent'
}
}]
}
}
});
})
function getChartData(json) {
var labels = [];
var omx_dataset = {
label: 'OMXS30',
borderColor: '#eb8484',
fill: false,
pointRadius: 0,
borderWidth: 1,
data: []
};
var gadd_dataset = {
label: "GADD SMP SEK",
borderColor: '#001F5B',
fill: false,
pointRadius: 0,
borderWidth: 1,
data: []
};
var mark1_dataset = {
label: 'Start',
borderWidth: 2,
borderColor: '#FF0000',
fill: false,
type: 'scatter',
data: [{
x: '2011-03-29',
y: 0
}],
pointRadius: 12
};
var mark2_dataset = {
label: '2016',
borderWidth: 2,
borderColor: '#FF0000',
fill: false,
type: 'scatter',
data: [{
x: '2015-12-31',
y: 20
}],
pointRadius: 12
};
var mark3_dataset = {
label: '2020',
borderWidth: 2,
borderColor: '#FF0000',
fill: false,
type: 'scatter',
data: [{
x: '2020-01-07',
y: 59
}],
pointRadius: 12
};
var mark4_dataset = {
label: '2022',
borderWidth: 2,
borderColor: '#FF0000',
fill: false,
type: 'scatter',
data: [{
x: '2022-01-03',
y: 109
}],
pointRadius: 12
};
json.map((point,i) => {
labels.push(point.Date)
const gadd_num = (point['GADD SMP SEK']*100).toFixed(2)
const omx_num = (point['OMXS30']*100).toFixed(2)
gadd_dataset.data.push(gadd_num)
omx_dataset.data.push(omx_num)
//console.log(point.Date)
//result1_dataset.data.push(point.date)
})
return {
labels: labels,
datasets: [omx_dataset, gadd_dataset, mark1_dataset, mark2_dataset, mark3_dataset, mark4_dataset]
}
}
Right now it shows all the datalabels from all the datasets and it looks messed up,
Thanks in advance
You could add the datalabels config to the line datasets, disabling the plugin, as the following:
var omx_dataset = {
label: 'OMXS30',
borderColor: '#eb8484',
fill: false,
pointRadius: 0,
borderWidth: 1,
data: []
datalabels: {
display: false
}
};
var gadd_dataset = {
label: "GADD SMP SEK",
borderColor: '#001F5B',
fill: false,
pointRadius: 0,
borderWidth: 1,
data: [],
datalabels: {
display: false
}
};
I have the following toy data:
export const playData = {
datasets: [
{
label: 'Dataset 1',
showLine: true,
data: [{ x: 1, y: 10, name: 'John' }, { x: 2, y: 5, name: 'Linda' }, { x: 3, y: 7, name: 'Erin' }, { x: 4, y: 4, name: 'Chloe' }, { x: 5, y: 8, name: 'Paul' }],
borderColor: 'rgb(255, 99, 132)',
backgroundColor: 'rgba(255, 99, 132, 0.5)',
},
};
I am then trying to make a custom tooltip showing this data, which works as expected:
export function Chart(props: { chartData: ChartData }) {
return <Scatter
data={props.chartData}
options={{
responsive: true,
scales: {
x: {
title: {
display: true,
text: 'Age (years)'
},
},
y: {
title: {
display: true,
text: 'Change in Height (inches)'
}
}
},
plugins: {
legend: {
position: 'top' as const,
},
tooltip: {
callbacks: {
label: (context) => {
return [context.raw.name, `age: ${context.parsed.x} years`, `height change: ${context.parsed.y} in`]
}
}
}
},
}}
/>;
}
But TS underlines the final context.raw.name and says
Property 'name' does not exist on type 'unknown'.ts(2339)
How can I fix this?
You can create your own custom interface by extending the one that is already being used and add the custom option you added to your dataset:
import { Chart, TooltipItem } from 'chart.js';
interface CustomTooltipItem extends TooltipItem<'scatter'> {
raw: {
x: number,
y: number,
name: string,
}
}
const ctx = document.getElementById("myChart") as HTMLCanvasElement;
const myChart = new Chart(ctx, {
type: 'scatter',
data: {
datasets: [{
label: '# of Votes',
data: [{ x: 1, y: 10, name: 'John' }, { x: 2, y: 5, name: 'Linda' }, { x: 3, y: 7, name: 'Erin' }, { x: 4, y: 4, name: 'Chloe' }, { x: 5, y: 8, name: 'Paul' }],
}]
},
options: {
plugins: {
tooltip: {
callbacks: {
label: (context: CustomTooltipItem) => {
return [context.raw.name, `age: ${context.parsed.x} years`, `height change: ${context.parsed.y} in`]
}
}
}
}
}
});
https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgYQBYENYBo4BUIQA2MwYAkjAKYhxwC+cAZlBDQOQDGGsAdAFYBnNgG4AsACgJwAHZUojdB0ooArgJit8REuSo1KADyrSAJgLwFipCtQA8bAR3Qw5bAHyIJtKOgDuALk9xWloDQOkVEAAjSigsLxCAT3DImLiE2ml0EEpA9SgZAHN44PoJOgkJDghpdTgOGAM4AF44EwgOSMpZHkLKGABRQmpumAAhRLITAAoAIhBEtEwYWYBKOHRzAAlcAFkAGWR0aQA3TaGR2TFxatr4BaXYFrhpSl8UbhhphoMcBAy4DBEmBcnAHE4XLE2CUQm1nOhAv9SrCTPCBP0BIEANpI2GwwjoGKEQJsADEcAgjDgADUIFQhDC8bRUTAEXAcXAwnAAIw4ZI8gAMOCyORJACkIKhpGx6H9OYEAEx8wIAVmF2VBbH2MlRMrocq5AGZlXAAOzq0VggYFaWyxDyuAAFhNzpeGpJaEIEEoeoNqpNAA4LZqAAroFSEPUAXUZsLoUYB+oBEDAJBqmKCTLAhBUhRkGdxTMBlh0iIBTKchEIUUUAGsC+WiwSiYFvjUqEZAsg1BoQForLpqOtmh5C0W8VB+iooNJ2bcOzAeD5fDwRZQcAADdB9QIAEgQ88Mi7AmHRJh4BgYiUop43m9QlGAhVQ8C4xx3cH3h6MPBPUDPPCJAwMgbgmyLjmU4FMhUUEhDB0GJuUqzCEAA
I have a chart that shows various data points.
Some of these data points are high numbers and some are low numbers.
The low numbers (visits) I can scale to a different scale and this new scale can be put on the "X" axis (It's the "Y" axis and then rotated 90degrees). But the problem is:
The grid remains even when removed
The
How can I extrapolate the poistion on the graph without adjusting the label data on hover?2 I have search Stackoverflow and ChartJS documentation but can't see how this can be done.
I was trying to use the "other" axis (in this case the top horizontal bar of the chart) so that the scale would be relative and raw data editing would not be needed but I can't get that to work and can't find documentation on this. I'm sure it's possible but I can't see how where.
I have found this question but this related only to ChartJS V2 .
Current version used is ChartJS 3.2.1
Original Version:
var ctx = document.getElementById("historicChart");
var historicChart = new Chart(ctx, {
type: "horizontalBar",
data: {
labels: [2022,2021,2020,2019,2018,2017,2016],
datasets: [
{
type: 'line',
label: 'Visits',
data: ["1","7","493","163","467","88","48"],
backgroundColor: '#FFC900',
pointBackgroundColor: '#FFC900',
pointRadius: 8,
pointStyle: 'circle',
showLine: false,
order: 1,
hoverRadius: 10
},
{
type: 'bar',
label: 'Applied',
data: ["486","800","704","1084","532","618","543"],
backgroundColor: '#436BFF',
borderWidth: 0,
order: 2,
barPercentage: 0.9,
stack: 'stack 0',
},
{
type: 'bar',
label: 'Accepted',
data: ["1","147","290","521","233","306","271"],
backgroundColor: '#C40500',
borderWidth: 0,
barPercentage: 0.9,
stack: 'stack 1',
order: 4
},
{
type: 'bar',
label: 'Declined',
data: ["616","4273","3998","3400","922","1225","1184"],
/*backgroundColor: '#03570c', /* emerald */
backgroundColor: '#006545', /* jade */
borderWidth: 0,
barPercentage: 0.9,
stack: 'stack 0',
order: 5
},
{
type: 'bar',
label: 'Processing',
data: ["6","13","22","1","34","2","1"],
backgroundColor: '#65ccD3', /* aqua + blue */
borderWidth: 0,
barPercentage: 0.9,
stack: 'stack 0',
order: 3
},
]
},
options: {
responsive: true,
interaction: {
intersect: false,
mode: 'index',
axis: 'y'
},
indexAxis: 'y',
scales: {
xAxes: [{
stacked: true,
ticks: {
stepSize: 1
},
beginAtZero: true
}],
yAxes: [{
stacked: true
}]
}
}
}
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.2.1/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="historicChart"></canvas>
UPDATE:
I have tried to add a custom secondary axis as per this example from the CHartJS documentation, but it doesn't quite do as I need; the grid lines remain and the axis scale is on the "wrong" side (two scales on one size (bottom) and zero scales on the other side (top)
var ctx = document.getElementById("historicChart");
var historicChart = new Chart(ctx, {
type: "horizontalBar",
data: {
labels: [2022,2021,2020,2019,2018,2017,2016],
datasets: [
{
type: 'line',
label: 'Visits',
data: ["1","7","493","163","467","88","48"],
backgroundColor: '#FFC900',
pointBackgroundColor: '#FFC900',
pointRadius: 8,
pointStyle: 'circle',
showLine: false,
order: 1,
hoverRadius: 10,
xAxisID:'xTwo'
},
{
type: 'bar',
label: 'Applied',
data: ["486","900","724","1084","532","618","543"],
backgroundColor: '#436BFF',
borderWidth: 0,
order: 2,
barPercentage: 0.9,
stack: 'stack 0',
},
{
type: 'bar',
label: 'Accepted',
data: ["1","147","290","511","253","306","271"],
backgroundColor: '#C40500',
borderWidth: 0,
barPercentage: 0.9,
stack: 'stack 1',
order: 4
},
{
type: 'bar',
label: 'Declined',
data: ["616","4373","3998","3400","922","1205","1184"],
/*backgroundColor: '#03570c', /* emerald */
backgroundColor: '#006545', /* jade */
borderWidth: 0,
barPercentage: 0.9,
stack: 'stack 0',
order: 5
},
{
type: 'bar',
label: 'Processing',
data: ["6","23","22","1","4","2","1"],
backgroundColor: '#65ccD3', /* aqua + blue */
borderWidth: 0,
barPercentage: 0.9,
stack: 'stack 0',
order: 3
},
]
},
options: {
responsive: true,
interaction: {
intersect: false,
mode: 'index',
axis: 'y'
},
indexAxis: 'y',
scales: {
xAxes: [{
stacked: true,
ticks: {
stepSize: 1
},
beginAtZero: true
}],
yAxes: [{
stacked: true
}],
xTwo: [{
type: 'linear',
display: true,
position: 'top',
grid: {
display: false,
drawOnChartArea: false,
ticks: {
display: false
}
}
}]
}
}
}
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.2.1/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="historicChart"></canvas>
The issue appears to be with the scale; "xTwo";
xTwo: [{
type: 'linear',
display: true,
position: 'top',
grid: {
display: false,
drawOnChartArea: false,
ticks: {
/* trying to even just hide the ticks here fails */
display: false,
}
}
}]
How can I fix this to hide the grid lines and put the scale on the correct side of the graph?
You can use a custom label callback for this in the tooltip config, also your scale config was wrong. It was in V2 style. For all changes please read the migration guide
var ctx = document.getElementById("historicChart");
var historicChart = new Chart(ctx, {
type: "horizontalBar",
data: {
labels: [2022, 2021, 2020, 2019, 2018, 2017, 2016],
datasets: [{
type: 'line',
label: 'Visits',
data: ["5", "35", "2465", "815", "2335", "440", "240"],
backgroundColor: '#FFC900',
pointBackgroundColor: '#FFC900',
pointRadius: 8,
pointStyle: 'circle',
showLine: false,
order: 1,
hoverRadius: 10
},
{
type: 'bar',
label: 'Applied',
data: ["486", "800", "704", "1084", "532", "618", "543"],
backgroundColor: '#436BFF',
borderWidth: 0,
order: 2,
barPercentage: 0.9,
stack: 'stack 0',
},
{
type: 'bar',
label: 'Accepted',
data: ["1", "147", "290", "521", "233", "306", "271"],
backgroundColor: '#C40500',
borderWidth: 0,
barPercentage: 0.9,
stack: 'stack 1',
order: 4
},
{
type: 'bar',
label: 'Declined',
data: ["616", "4273", "3998", "3400", "922", "1225", "1184"],
/*backgroundColor: '#03570c', /* emerald */
backgroundColor: '#006545',
/* jade */
borderWidth: 0,
barPercentage: 0.9,
stack: 'stack 0',
order: 5
},
{
type: 'bar',
label: 'Processing',
data: ["6", "13", "22", "1", "34", "2", "1"],
backgroundColor: '#65ccD3',
/* aqua + blue */
borderWidth: 0,
barPercentage: 0.9,
stack: 'stack 0',
order: 3
},
]
},
options: {
responsive: true,
interaction: {
intersect: false,
mode: 'index',
axis: 'y'
},
indexAxis: 'y',
plugins: {
tooltip: {
callbacks: {
label: (ttItem) => {
const label = ttItem.dataset.label;
const val = Number(ttItem.raw);
let res = `${label}: ${label === 'Visits' ? val / 5 : val}`;
return res
}
}
}
},
scales: {
x: {
stacked: true,
ticks: {
stepSize: 1
},
beginAtZero: true
},
y: {
stacked: true
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.2.1/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="historicChart"></canvas>
UPDATE:
You tried adding a second scale, in chart.js V3 all scales are their own objects and not arrays anymore, for all changes see migration guide linked above. Changing the scales to objects fixes your issue in your updated approach without needing to multiply values :
var ctx = document.getElementById("historicChart");
var historicChart = new Chart(ctx, {
type: "horizontalBar",
data: {
labels: [2022, 2021, 2020, 2019, 2018, 2017, 2016],
datasets: [{
type: 'line',
label: 'Visits',
data: ["1", "7", "493", "163", "467", "88", "48"],
backgroundColor: '#FFC900',
pointBackgroundColor: '#FFC900',
pointRadius: 8,
pointStyle: 'circle',
showLine: false,
order: 1,
hoverRadius: 10,
xAxisID: 'xTwo'
},
{
type: 'bar',
label: 'Applied',
data: ["486", "900", "724", "1084", "532", "618", "543"],
backgroundColor: '#436BFF',
borderWidth: 0,
order: 2,
barPercentage: 0.9,
stack: 'stack 0',
},
{
type: 'bar',
label: 'Accepted',
data: ["1", "147", "290", "511", "253", "306", "271"],
backgroundColor: '#C40500',
borderWidth: 0,
barPercentage: 0.9,
stack: 'stack 1',
order: 4
},
{
type: 'bar',
label: 'Declined',
data: ["616", "4373", "3998", "3400", "922", "1205", "1184"],
/*backgroundColor: '#03570c', /* emerald */
backgroundColor: '#006545',
/* jade */
borderWidth: 0,
barPercentage: 0.9,
stack: 'stack 0',
order: 5
},
{
type: 'bar',
label: 'Processing',
data: ["6", "23", "22", "1", "4", "2", "1"],
backgroundColor: '#65ccD3',
/* aqua + blue */
borderWidth: 0,
barPercentage: 0.9,
stack: 'stack 0',
order: 3
},
]
},
options: {
responsive: true,
interaction: {
intersect: false,
mode: 'index',
axis: 'y'
},
indexAxis: 'y',
scales: {
x: {
stacked: true,
ticks: {
stepSize: 1000
},
beginAtZero: true
},
y: {
stacked: true
},
xTwo: {
type: 'linear',
display: true,
position: 'top',
grid: {
display: false,
ticks: {
display: false
}
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.2.1/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="historicChart"></canvas>
I'm trying to remove a dataset from label:
But the label callback is called twice, so i don't know how to do it.
I was expecting an array in the callback and that you can simply remove the one not needed.
here's what i have so far and tried:
var labels = [];
for (let index = 0; index < 12; index++) {
labels.push(index);
}
window.onload = function () {
var canvas = document.getElementById('elm-chart'),
ctx = canvas.getContext('2d');
var myLineChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: '-15',
data: [
{
x: 0,
y: 10,
},
{
x: 1,
y: 20,
},
],
borderColor: 'red',
},
{
label: '15',
data: [
{
x: 1,
y: 20,
},
{
x: 2,
y: 30,
},
],
borderColor: 'blue',
},
{
label: '25',
data: [
{
x: 2,
y: 30,
},
{
x: 3,
y: 35,
},
],
borderColor: 'yellow',
},
{
label: '-15',
data: [
{
x: 6,
y: -10,
},
{
x: 7,
y: -20,
},
],
borderColor: 'red',
},
{
label: '15',
data: [
{
x: 7,
y: -20,
},
{
x: 8,
y: -30,
},
],
borderColor: 'blue',
},
{
label: '25',
data: [
{
x: 8,
y: -30,
},
{
x: 9,
y: -35,
},
],
borderColor: 'yellow',
},
],
},
options: {
responsive: true,
plugins: {
legend: {
onClick: (evt, legendItem, legend) => {
let newVal = !legendItem.hidden;
legend.chart.data.datasets.forEach((dataset) => {
if (dataset.label === legendItem.text) {
dataset.hidden = newVal;
}
});
legend.chart.update();
},
labels: {
filter: (legendItem, chartData) => {
let entries = chartData.datasets.map((e) => e.label);
return entries.indexOf(legendItem.text) === legendItem.datasetIndex;
},
},
},
},
scales: {
x: {
type: 'linear',
ticks: {
stepSize: 30,
},
},
},
plugins: {
tooltip: {
callbacks: {
title: function (context) {
return [`test`, 'test2'];
},
label: function (context) {
console.log(context.dataset.label);
console.log(context.formattedValue);
console.log(context);
return [
`${context.dataset.label}:${context.formattedValue}`,
'test',
];
},
},
},
},
},
});
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.2.1/chart.min.js"></script>
<canvas id="elm-chart" width="640" height="480"></canvas>
So to be clear i don't want to remove the datapoint in the dataset. I just want to remove 1 datapoint from the label, the top one always
What you can do is instead of using the default tooltip instead use a html tooltip.
In the JS that makes the tooltip instead of looping over each active item you only take the first one and display that.
Official sample of html tooltip: https://www.chartjs.org/docs/master/samples/tooltip/html.html
I need to show the last bar value only. like this image:
I try this code but it shows all values.
var dataX = {
labels: [['Personne', 'seule'], ['Couple sans', 'enfant'], ['Couple avec', 'enfant(s)'], ['Famille', 'monoparentale'], 'Autres'],
datasets: [{
label: 'Data 1',
data: [33,28,25,8,2.5],
backgroundColor: '#00BBF1',
borderWidth: 0
},
{
label: 'Data 2',
data: [29,30,30,8,2],
backgroundColor: '#3CC6F4',
borderWidth: 0
},
{
label: 'Data 3',
data: [41,22,22,11,3],
backgroundColor: '#92D9F8',
borderWidth: 0
}]
};
var optionsX = {
tooltips: {
enabled: false
},
responsive: true,
maintainAspectRatio: false,
legend: false,
scales: {
xAxes: [{
gridLines : {
color: "#fff"
},
}],
yAxes: [{
gridLines : {
display : false
},
ticks: {
min: 0,
max: 50,
stepSize: 10,
callback: function(value) {
return value + "%"
},
}
}]
},
plugins: {
datalabels: {
color: '#59595B',
font: {
weight: 'bold',
size: 14,
},
align: 'end',
anchor: 'end',
formatter: function(value, context) {
return value +'%';
}
}
},
};
var ctx = document.getElementById('chart-one');
var myChart = new Chart(ctx, {
type: 'bar',
data: dataX,
options: optionsX
});
You can change the plugins.datalabels.formatter function as follows:
plugins: {
...
datalabels: {
formatter: (value, context) => context.datasetIndex == 2 ? value + '%' : ''
}
}
Please take a look at your amended code below and see how it works.
var dataX = {
labels: [
['Personne', 'seule'],
['Couple sans', 'enfant'],
['Couple avec', 'enfant(s)'],
['Famille', 'monoparentale'], 'Autres'
],
datasets: [{
label: 'Data 1',
data: [33, 28, 25, 8, 2.5],
backgroundColor: '#00BBF1',
borderWidth: 0
},
{
label: 'Data 2',
data: [29, 30, 30, 8, 2],
backgroundColor: '#3CC6F4',
borderWidth: 0
},
{
label: 'Data 3',
data: [41, 22, 22, 11, 3],
backgroundColor: '#92D9F8',
borderWidth: 0
}
]
};
var optionsX = {
tooltips: {
enabled: false
},
responsive: true,
maintainAspectRatio: false,
legend: false,
scales: {
xAxes: [{
gridLines: {
color: "#fff"
},
}],
yAxes: [{
gridLines: {
display: false
},
ticks: {
min: 0,
max: 50,
stepSize: 10,
callback: function(value) {
return value + "%"
},
}
}]
},
plugins: {
datalabels: {
color: '#59595B',
font: {
weight: 'bold',
size: 14,
},
align: 'end',
anchor: 'end',
formatter: (value, context) => context.datasetIndex == 2 ? value + '%' : ''
}
},
};
var ctx = document.getElementById('chart-one');
var myChart = new Chart(ctx, {
type: 'bar',
data: dataX,
options: optionsX
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels"></script>
<canvas id="chart-one"></canvas>