Chart.js bubble chart changing dataset labels - javascript

Is it possible to change the dataset labels that show up in the tooltip for a bubble chart.js chart?
As it stands right now, the dataset is based off the x,y,r values, but I'd like to inject some additional content, so that instead of reading (5,55,27.5) it reads something like: (Day:5, Total:55). I'd like to leave off the radius of 27.5 in the tooltip.

Yes! It is possible.
To achieve that, you can use the following tooltip­'s label callback function :
tooltips: {
callbacks: {
label: function(t, d) {
return d.datasets[t.datasetIndex].label +
': (Day:' + t.xLabel + ', Total:' + t.yLabel + ')';
}
}
}
ᴡᴏʀᴋɪɴɢ ᴇxᴀᴍᴘʟᴇ
var chart = new Chart(ctx, {
type: 'bubble',
data: {
datasets: [{
label: 'Bubble',
data: [{
x: 5,
y: 55,
r: 27.5
}],
backgroundColor: 'rgba(0, 119, 290, 0.6)'
}]
},
options: {
tooltips: {
callbacks: {
label: function(t, d) {
return d.datasets[t.datasetIndex].label +
': (Day:' + t.xLabel + ', Total:' + t.yLabel + ')';
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="ctx"></canvas>

The previous answer doesn't work as of Chart.JS V3 because of these changes:
https://github.com/chartjs/Chart.js/blob/master/docs/migration/v3-migration.md
The following code works in 4.1.1:
var chart = new Chart(ctx, {
type: 'bubble',
data: {
datasets: [{
label: 'Bubble',
data: [{
x: 5,
y: 55,
r: 27.5
}],
backgroundColor: 'rgba(0, 119, 290, 0.6)'
}]
},
options: {
plugins: {
tooltip: {
callbacks: {
label: function(item) {
return item.raw.r
}
}
}
}
}
});
<script src="https://npmcdn.com/chart.js#latest/dist/chart.umd.js"></script>
<canvas id="ctx"></canvas>

Related

add a unit to label with chartjs plugin datalabels

I am opening a new question following Rohit's answer on this question Chart.js Show labels on Pie chart
Is it possible to modify the labels with the plugin chartjs datalabels, for exemple add a unit to it?
Thanks for your answer!
EDIT:
Following Ty answer, I tried this:
if(type==='pie'){
GraphOpt['plugins'] = {
datalabels: {
formatter: (item) => {
return item + ' ' + label_unit;
}
}
}
}
else{
GraphOpt['tooltips'] = {
callbacks: {
label: (item) => `${item.yLabel} ${label_unit}`,
}
}
}
but it's still not showing the units on the graph.
Also, I can't get rid of the values showing on bar graphes with the plugin
EDIT2: My graph function
function setChart(Graph, ctx, type, graph_data, labels, title, label_graph, label_unit){
var GraphOpt = {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
},
title: {
display:true,
text: title,
},
tooltips: {},
plugins : {}
}
//graph config
var Graph_config = {
type: type,
data: {
labels: labels,
datasets: [{
label: label_graph,
data: graph_data,
backgroundColor: backgroundColor,
borderColor: backgroundColor,
borderWidth: 1
}]
},
options: GraphOpt,
}
if(type==='pie'){
GraphOpt['plugins'] = {
datalabels: {
formatter: (item) => {
return item + ' ' + label_unit;
}
}
}
}
else{
GraphOpt['tooltips'] = {
callbacks: {
label: (item) => `${item.yLabel} ${label_unit}`,
}
}
}
//create a new graph if graph doesn't exist (init)
console.log(typeof window[Graph])
if(typeof window[Graph] === "undefined") {
window[Graph] = new Chart(ctx, Graph_config);
console.log(typeof window[Graph])
//update if graph exist
}else{
console.log('Graph Update')
//updating with new chart data
window[Graph].config=Graph_config;
window[Graph].title=title;
//redraw the chart
window[Graph].update();
}
}
Yes, have a look at the chartjs-plugin-datalabels documentation. In particular, you want to use the options.plugins.datalabels.formatter option to specify a custom function and append units to every label string.
Here's an example Chart.js config that adds a unit to every data label:
{
type: 'pie',
data: {
datasets: [
{
data: [84, 28, 57, 97],
backgroundColor: [
'rgb(255, 99, 132)',
'rgb(255, 159, 64)',
'rgb(255, 205, 86)',
'rgb(75, 192, 192)',
],
label: 'Dataset 1',
},
],
labels: ['Red', 'Orange', 'Yellow', 'Green'],
},
options: {
plugins: {
datalabels: {
formatter: (val) => {
return val + ' kg';
}
}
}
}
}

Chart.js Multiple charts with one common legend

There are multiple charts on one page.
Each chart line is common.
I want to display a legend that is common to multiple charts like the figure.It shows and hides all chart lines with OnClick like the default legend.
THIS PICT IS FAKE
Is that possible? how?
I had tried Chart.js sync legend toggle on multiple charts, One legend, multiple charts Chart JS and etc.
But, those solutions have one chart with legend, and that legend affects other charts.
Should I hide the chart and show only the legend?
Should I draw a chart with no data?
I would be grad if you could tell me
HTML
<script src="https://rawgit.com/nnnick/Chart.js/v1.0.2/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.0.0-beta/Chart.js"></script>
<div>
<canvas id="myChartA"></canvas>
</div>
<div>
<canvas id="myChartB"></canvas>
</div>
JS
var ctxA = document.getElementById("myChartA").getContext("2d");
var ctxB = document.getElementById("myChartB").getContext("2d");
let data_A1 = [{
x: "2019-01-01 00:01:38",
y: "13.0"
},
{
x: "2019-01-01 01:01:39",
y: "11.0"
},
{
x: "2019-01-01 02:01:40",
y: "16.0"
},
{
x: "2019-01-01 03:01:41",
y: "15.0"
},
{
x: "2019-01-01 04:01:42",
y: "14.0"
}
];
var data_A2 = [{
x: "2019-01-01 00:01:42",
y: 14.671
}, {
x: "2019-01-01 01:01:42",
y: 13.691
}, {
x: "2019-01-01 02:01:42",
y: 16.691
}, {
x: "2019-01-01 03:01:42",
y: 17.691
}, {
x: "2019-01-01 04:01:42",
y: 18.691
}];
let data_B1 = [{
x: "2019-01-02 00:01:38",
y: "12.0"
},
{
x: "2019-01-02 01:01:39",
y: "11.0"
},
{
x: "2019-01-02 02:01:40",
y: "13.0"
},
{
x: "2019-01-02 03:01:41",
y: "14.0"
},
{
x: "2019-01-02 04:01:42",
y: "16.0"
}
];
var data_B2 = [{
x: "2019-01-02 00:00:00",
y: 14.671
}, {
x: "2019-01-02 01:01:42",
y: 13.691
}, {
x: "2019-01-02 02:01:42",
y: 16.691
}, {
x: "2019-01-02 03:01:42",
y: 15.691
}, {
x: "2019-01-02 04:01:42",
y: 14.691
}];
var myChartA = new Chart(ctxA, {
type: 'line',
data: {
datasets: [{
label: '1st Data',
data: data_A1,
borderColor: '#0f0',
showLine: true
}, {
label: '2nd Data',
data: data_A2,
borderColor: '#f00',
showLine: true
}]
},
options: {
scales: {
xAxes: [{
type: 'time',
time: {
displayFormat: 'h:mm',
}
}]
}
}
});
var myChartB = new Chart(ctxB, {
type: 'line',
data: {
datasets: [{
label: '1st Data',
data: data_B1,
borderColor: '#0f0',
showLine: true
}, {
label: '2nd Data',
data: data_B2,
borderColor: '#f00',
showLine: true
}]
},
options: {
scales: {
xAxes: [{
type: 'time',
time: {
displayFormat: 'h:mm',
}
}]
}
}
});
You can create a common legend and through generateLegend api, if both the datasets are similar.
First disable the default legends though the options
legend: {
display: false
}
Then use generateLegend() api to get the data labels and set it to a common element.
<ul class="legend">
</ul>
Then add event listeners to the generated elements and target all the charts
document.querySelector('.legend').innerHTML = myChartA.generateLegend();
var legendItems = document.querySelector('.legend').getElementsByTagName('li');
for (var i = 0; i < legendItems.length; i++) {
legendItems[i].addEventListener("click", legendClickCallback.bind(this,i), false);
}
function legendClickCallback(legendItemIndex){
document.querySelectorAll('.myChart').forEach((chartItem,index)=>{
var chart = Chart.instances[index];
var dataItem = chart.data.datasets[legendItemIndex]
if(dataItem.hidden == true || dataItem.hidden == null){
dataItem.hidden = false;
} else {
dataItem.hidden = true;
}
chart.update();
})
}
A sample pen is present here
https://codepen.io/srajagop/pen/yLBJOOo
Note I am using chartjs 2.8
If anyone using React version of Chartjs particularly react-chartjs-2, I have done it using React Hooks with react-chartjs-2, see the sandbox demo

Chart.js how to modify an existing legend

How do I modify an existing legend in Chart.js?
I've seen complex methods of creating a custom HTML legend (using generateLegend or legendCallback), and a simple method of options.legend.legendText which should accept an array, but saw no change so assumed that was for version 1.
I'm looking to add text to the default legend:
type: 'doughnut',
data: {
datasets: [{
data: series,
}],
labels: labels,
},
options: {
legend: {
legendText = labels.map((label, index) => `${label} - ${series[index]}%`);
}
}
Edit:
I noticed that if the chart was redrawn (e.g. if the browser window is resized) the legend would lose the extra text.
I've modified the approach to work as an inline plugin so that the label object is modified before the legend is drawn.
let labels = ['a', 'b', 'c', 'd'],
series = [4, 2, 1, 3],
myChart = new Chart(document.getElementById('chart'), {
type: 'doughnut',
data: {
labels: labels,
datasets: [{
data: series,
backgroundColor: ['red', 'blue', 'green', 'orange']
}]
},
options: {
maintainAspectRatio: false
},
plugins: [{
afterLayout: function(chart) {
let total = chart.data.datasets[0].data.reduce((a, b) => {
return a + b;
});
chart.legend.legendItems.forEach(
(label) => {
let value = chart.data.datasets[0].data[label.index];
label.text += ' - ' + (value / total * 100).toFixed(0) + '%'
return label;
}
)
}
}]
});
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<canvas id="chart"></canvas>
Chart.js 3.xx
I've included a sample for version 3.5 too.
You can alter the legend text by overriding the generateLabels method.
let labels = ['a', 'b', 'c', 'd'],
series = [4, 2, 1, 3],
myChart = new Chart(document.getElementById('chart'), {
type: 'doughnut',
data: {
labels: labels,
datasets: [{
data: series,
backgroundColor: ['red', 'blue', 'green', 'orange']
}]
},
options: {
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: "bottom",
align: "center",
fontFamily: "Arial",
labels: {
usePointStyle: true,
fontColor: "red",
generateLabels(chart) {
const data = chart.data;
if (data.labels.length && data.datasets.length) {
const {labels: {pointStyle}} = chart.legend.options;
return data.labels.map((label, i) => {
const meta = chart.getDatasetMeta(0);
const style = meta.controller.getStyle(i);
return {
text: 'This is ' + label + ' - ' + chart.data.datasets[0].data[i],
fillStyle: style.backgroundColor,
strokeStyle: style.borderColor,
lineWidth: style.borderWidth,
pointStyle: pointStyle,
hidden: !chart.getDataVisibility(i),
index: i
};
});
}
return [];
}
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.min.js"></script>
<canvas id="chart"></canvas>

ChartJs Bubble chart - on hover bubble becomes too big

I created a bubble chart using ChartJs and populating data dynamically using Json.
See the code below.
for (var i = 0; i < response.data.length; i++) {
var point_data = [];
point_data.push({
x: response.data[i]['return_tickets'].toString(),
y: Math.round(response.data[i]['return_percentage']).toString(),
r: Math.round((response.data[i]['return_percentage'])).toString()
});
data.push({ label: response.data[i]['staff_name'], data: point_data, backgroundColor: getRandomColor(), hoverRadius:4 });
}
new Chart(document.getElementById("bubbleChart"), {
type: 'bubble',
data: {
datasets: data
},
options: {
title: {
display: true,
text: ''
}, scales: {
yAxes: [{
scaleLabel: {
display: true,
labelString: "Return Tickets %"
}
}],
xAxes: [{
scaleLabel: {
display: true,
labelString: "Return Tickets"
}
}]
}
}
});
It generates the desired chart as below
The problem is when I hover over any bubble the size of the bubble increases exponentially.
How to keep the size same ?
I'm setting the hoverRadius property of the dataset but it does nothing for me.
Problem is with your this line of code:
{ label: response.data[i]['staff_name'], data: point_data, backgroundColor: getRandomColor(), hoverRadius:4 }
This is not a valid JSON. Values must be either strings or arrays. Most probably issue is at label: response.data[i]['staff_name'] or in point_data (I can see you are making x, y and r values .toString() that maybe not required). Check it again. Create a valid JSON and then try by setting hoverRadius: 0, it will work.
Setting hoverRadius: 0 working fine for me. Bubble size will not change on mouse over if you set hoverRadius: 0.
Below is working example:
var chart = new Chart(ctx, {
type: 'bubble',
data: {
datasets: [{
label: 'Bubble',
data: [{
x: 5,
y: 55,
r: 27.5
}],
backgroundColor: 'rgba(0, 119, 290, 0.6)',
hoverRadius: 0
}]
},
options: {
tooltips: {
callbacks: {
label: function(t, d) {
return d.datasets[t.datasetIndex].label +
': (Day:' + t.xLabel + ', Total:' + t.yLabel + ')';
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<canvas id="ctx"></canvas>
Checkout official documentation for more info : https://www.chartjs.org/docs/latest/charts/bubble.html#dataset-properties
I have already faced the same issue also fixed it by typecasting for every x,y & z. I just convert it to float
'x' => (float) $x_axis_value,
'y' => (float) $y_axis_value,
'r' => (float) $radious_value,

Chart.js stacked and grouped horizontalBar chart

I've been trying to display somewhat complex data on my webpage and chose chart.js to do so.
Therefor I need to group multiple stacked bars horizontally.
I already found this fiddle for "normal" bars but couldn't quite change it to work with horizontalBar yet.
Stackoverflow question: Chart.js stacked and grouped bar chart
The original Fiddle (http://jsfiddle.net/2xjwoLq0/) has
Chart.defaults.groupableBar = Chart.helpers.clone(Chart.defaults.bar);
And I just replaced the .bar everywhere in the code with .horizontalBar (well knowing that this won't make the cut).
Chart.defaults.groupableBar = Chart.helpers.clone(Chart.defaults.horizontalBar);
Since that didn't quite work, I tried adding the second stacked modifier as suggested for horizontal bars here:
Horizontal stacked bar chart with chart.js and flipped the functions for X and Y calculation (calculateBarY/calculateBarX)
Which quite work either because the stacks won't get merged onto each other correctly.
http://jsfiddle.net/2xjwoLq0/3/
I would appreciate if anyone could help me out on this one.
Looking for something similar, I took a look on example you gave, and decide to write something.
Rather than trying to fix the code or reusing the 'groupableBar', I get Chart.js code from Chart.controllers.horizontalBar and rewrite some part in functions calculateBarY, calculateBarHeight.
Just reused the getBarCount function from your example.
Chart.defaults.groupableHBar = Chart.helpers.clone(Chart.defaults.horizontalBar);
Chart.controllers.groupableHBar = Chart.controllers.horizontalBar.extend({
calculateBarY: function(index, datasetIndex, ruler) {
var me = this;
var meta = me.getMeta();
var yScale = me.getScaleForId(meta.yAxisID);
var barIndex = me.getBarIndex(datasetIndex);
var topTick = yScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo);
topTick -= me.chart.isCombo ? (ruler.tickHeight / 2) : 0;
var stackIndex = this.getMeta().stackIndex;
if (yScale.options.stacked) {
if(ruler.datasetCount>1) {
var spBar=ruler.categorySpacing/ruler.datasetCount;
var h=me.calculateBarHeight(ruler);
return topTick + (((ruler.categoryHeight - h) / 2)+ruler.categorySpacing-spBar/2)+(h+spBar)*stackIndex;
}
return topTick + (ruler.categoryHeight / 2) + ruler.categorySpacing;
}
return topTick +
(ruler.barHeight / 2) +
ruler.categorySpacing +
(ruler.barHeight * barIndex) +
(ruler.barSpacing / 2) +
(ruler.barSpacing * barIndex);
},
calculateBarHeight: function(ruler) {
var returned=0;
var me = this;
var yScale = me.getScaleForId(me.getMeta().yAxisID);
if (yScale.options.barThickness) {
returned = yScale.options.barThickness;
}
else {
returned= yScale.options.stacked ? ruler.categoryHeight : ruler.barHeight;
}
if(ruler.datasetCount>1) {
returned=returned/ruler.datasetCount;
}
return returned;
},
getBarCount: function () {
var stacks = [];
// put the stack index in the dataset meta
Chart.helpers.each(this.chart.data.datasets, function (dataset, datasetIndex) {
var meta = this.chart.getDatasetMeta(datasetIndex);
if (meta.bar && this.chart.isDatasetVisible(datasetIndex)) {
var stackIndex = stacks.indexOf(dataset.stack);
if (stackIndex === -1) {
stackIndex = stacks.length;
stacks.push(dataset.stack);
}
meta.stackIndex = stackIndex;
}
}, this);
this.getMeta().stacks = stacks;
return stacks.length;
}
});
var data = {
labels: ["January", "February", "March"],
datasets: [
{
label: "Dogs",
backgroundColor: "rgba(255,0,0,0.2)",
data: [20, 10, 25],
stack: 1,
xAxisID: 'x-axis-0',
yAxisID: 'y-axis-0'
},
{
label: "Cats",
backgroundColor: "rgba(255,255,0,0.2)",
data: [70, 85, 65],
stack: 1,
xAxisID: 'x-axis-0',
yAxisID: 'y-axis-0'
},
{
label: "Birds",
backgroundColor: "rgba(0,255,255,0.2)",
data: [10, 5, 10],
stack: 1,
xAxisID: 'x-axis-0',
yAxisID: 'y-axis-0'
},
{
label: ":-)",
backgroundColor: "rgba(0,255,0,0.2)",
data: [20, 10, 30],
stack: 2,
xAxisID: 'x-axis-1',
yAxisID: 'y-axis-0'
},
{
label: ":-|",
backgroundColor: "rgba(0,0,255,0.2)",
data: [40, 50, 20],
stack: 2,
xAxisID: 'x-axis-1',
yAxisID: 'y-axis-0'
},
{
label: ":-(",
backgroundColor: "rgba(0,0,0,0.2)",
data: [60, 20, 20],
stack: 2,
xAxisID: 'x-axis-1',
yAxisID: 'y-axis-0'
},
]
};
var ctx = document.getElementById("myChart").getContext("2d");
new Chart(ctx, {
type: 'groupableHBar',
data: data,
options: {
scales: {
yAxes: [{
stacked: true,
type: 'category',
id: 'y-axis-0'
}],
xAxes: [{
stacked: true,
type: 'linear',
ticks: {
beginAtZero:true
},
gridLines: {
display: false,
drawTicks: true,
},
id: 'x-axis-0'
},
{
stacked: true,
position: 'top',
type: 'linear',
ticks: {
beginAtZero:true
},
id: 'x-axis-1',
gridLines: {
display: true,
drawTicks: true,
},
display: false
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.min.js"></script>
<canvas id="myChart"></canvas>
Also put example on jsfiddle here: https://jsfiddle.net/b7gnron7/4/
Code is not strongly tested, you might found some bugs especially if you try to display only one stacked group (use horizontalBar instead in this case).
Your post is a little bit old... not sure that you still need a solution, but it could be useful for others ^_^

Categories