SOLVED
Solution: Changed the spelling of axis to axes
I have just begun working with Chart.js as an alternative to the Python Plot.ly library.
So far I have been able to get the chart formatted the way I would like but there are still a couple of nuances I cant get right.
The first of which is that I cannot get any solution for adding a $ before Y-Axis values.
Here is my chart followed by the chart's code:
<script>
var ctx = document.getElementById("myChart").getContext('2d');
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var O1 = [1000,2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000];
var O2 = [3000,6000,5000,3000,8000,6000,4000,7000,8000,0000,3000,1000];
var mychart = new Chart(ctx, {
type: 'line',
options: {
title:{
text: 'Test Chart',
display: true,
fontStyle: 'bold',
fontSize: 16,
},
legend:{
position: 'right',
},
scales:{
yAxis:[{
scaleLabel:{
display: true,
labelString: 'Tester',
},
ticks:{
callback: function(value, index, values) {
if(parseInt(value) >= 1000){
return '$' + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
} else {
return '$' + value;
}
}
}
}]
},
tooltips:{
callbacks: {
label: function(tooltipItem, data){
var dataLabel = data.labels[tooltipItem.index];
var value = ': $ ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].toLocaleString();
if (Chart.helpers.isArray(dataLabel)) {
dataLabel = dataLabel.slice();
dataLabel[0] += value;
} else {
dataLabel += value;
}
return dataLabel1;
}
}
}
},
data: {
labels: months,
datasets: [
{
label: 'Officer1',
data: O1,
fill: false,
backgroundColor: 'rgba(255, 99, 132)',
borderColor: 'rgba(255, 99, 132)',
},
{
label: 'Officer2',
data: O2,
fill: false,
backgroundColor: 'rgba(0,0,255)',
borderColor: 'rgba(0,0,255)',
}
]
}
});
</script>
As you may be able to see I would simply like to add a $ before my Y-Axis data values. The last solution I tried also broke the data up by 1000s.
Additional:
The next few issues I've come across are very minor, which is why I chose to include them here.
First I would like a way to center my title over the chart itself, rather than centered over the whole chart+legend item.
Second is I would like to add some space to the left of my legend, as to separate it more from the chart.
Lastly, my Y-Axis title will not display. I would like it to display here while im testing in case I need it in the future.
This issue was solved by changing the spelling of axis to axes...subtle
Change the spelling of axis to axes
Related
i have a multilevel donut chart but it is not rendering correctly here is code
the problem is, onmouseover on all green parts it says objects, on all grey parts it says products, solution i would like is, on outer ring it should say products, in middle objects, and inner most should be materials, grey areas should just show number. here is a jsfiddle of the problem
Code:
var op=93;
var ap=99;
var mp=66;
var ctx = new Chart(myChart, {
type: 'doughnut',
data: {
labels: ['Objects', 'Products', 'Materials'],
datasets: [{
label: 'Objects',
data: [op, 100 - op],
backgroundColor: ['#006a4e','#eeeeee'],
hoverOffset: 4
},{
label: 'Products',
data: [ap, 100 - ap],
backgroundColor: ['#2e856e', '#eeeeee'],
hoverOffset: 4
},
{
label: 'Materials',
data: [mp, 100 - mp],
backgroundColor: ['#5ca08e', '#eeeeee'],
hoverOffset: 4
}
]
},
options: {
//cutoutPercentage: 40,
height: 200,
width:200
}
});
You can achieve that fairly simple with Chart.JS 2.7.2. Add labels to each dataset like this:
data: {
labels: ['Existing', 'Non'],
datasets: [
{
labels: ['Objects', 'Non-objects'],
...
}, {
labels: ['Products', 'Non-products'],
...
},
{
labels: ['Materials', 'Non-materials'],
...
}
]
}
And add the following label tooltip callback:
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var dataset = data.datasets[tooltipItem.datasetIndex];
var index = tooltipItem.index;
return dataset.labels[index] + ": " + dataset.data[index];
}
}
}
Demo: https://jsfiddle.net/adelriosantiago/fxd6vops/3/
I am sure it is possible with Chart.JS > 3.0 too but I have no idea how since quite a few things changed in the structure.
I had a same problem which took a lot of time but what worked in the end was importing these elements and initialising them ..
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
ChartJS.register(ArcElement, Tooltip, Legend);
these are the elements that helps make the entire chart. you can't skip them
When i draw a chart with 4 lines, each with its own data ofc, i programmatically create the options for the LineChart that has 4 Yaxis, first one on the left and the rest on the right side. Now, after the chart is drawn and i de-select some datasources from the list (less lines to draw), the now-obsolete yAxis ticks stay there, even when the chart correctly draws only the selected lines, and the options are updated as well correctly. I cant think of a way to remove them!
I have googled for 2 days and cant find a solution. I am using react in functional style and it makes things more complicated because every advice seems to be in the classic style.
I am using react-chartjs-2 wrapper as well, if this helps.
I am also quite new to react, and asking in Stackoverflow, so please cut me some slack :)
I assume the chart is being re-rendered or something because the amount of lines etc do change.
In the images, the "createYaxis" that is shown in the console.log is the generated yAxes- part of the options object (which is functional otherwise). The problem yAxises are on the right side in red and yellow. Images show before and after situation.
Image of the options-object generated by the code below the img:
var yAxisItems = [];
function createYaxises (num){
var arr = [];
for (var i=0;i<num.length;i++){
if (i===0){
arr.push({
display: true,
id: i,
type: 'linear',
position: 'left',
gridLines: {
display:false,
//color: 'blue'
},
ticks: {
fontColor: lineColourArray[i],
fontSize: 14,
}
})
}
else {
arr.push({
display: true,
id: i,
type: 'linear',
position: 'right',
gridLines: {
display:false,
//color: 'blue'
},
ticks: {
display:true,
fontColor: lineColourArray[i],
fontSize: 14,
}
})
}}
yAxisItems = arr;
console.log("createyaxis arr: " , arr);
console.log("createyaxis: " , yAxisItems); //JSON.stringify(yAxisItems));
}
//get data for selected sensors and set it to chart data
const handleGetSelectedSensorData = function () {
var d = getSelectedSensorData();
console.log("d: ", d);
var dSets = [];
if (d[0]){
d.map((dItem,index)=> {
var newDsetData =[];
if (dItem.data){
dItem.data.map((innerDataItem)=> {
var dSet = {};
dSet.x = innerDataItem.timestamp;
dSet.y = innerDataItem.v;
newDsetData.push(dSet);
})
var newset = {
data: newDsetData,
label: dItem.sensorTag,
borderColor: lineColourArray[index],
fill: false,
pointRadius: 1.5,
backgroundColor:lineColourArray[index],
borderWidth: 2,
showLine: true,
pointHoverRadius: 5,
lineTension: 1,
};
dSets.push(newset);
}})
var dDataTemp = {};
var optionsTemp = new Object();
dDataTemp.datasets =dSets;
//create yaxises only once
createYaxises(dDataTemp.datasets);
//more than one set (TODO)
//console.log("dDataTemp.datasets : ", dDataTemp.datasets)
if (dDataTemp.datasets.length >1){
console.log("dset > 1");
for(var i=0;i< dDataTemp.datasets.length;i++) {
dDataTemp.datasets[i].yAxisID = i;
console.log("setting options");
optionsTemp ={
tooltips: {
enabled: true,
intersect:false,
mode:'x',
callbacks: {
title: function(tooltipItem, data) {
var toSplit = tooltipItem[0].label.split(",");
return (toSplit[0]);
},
label: function (tooltipItem) {
var split = tooltipItem.xLabel.split(',');
//return ( Number(tooltipItem.yLabel).toFixed(3));
return (split[2] + " : " + Number(tooltipItem.yLabel).toFixed(3));
}
},
},
hover: {
mode: 'nearest',
intersect: true,
},
title:{
display:true,
text:'Valittu sensoridata',
fontSize:20
},
legend:{
display:true,
position:'right'
},
scales: {
xAxes: [{
display: true,
type: 'time',
ticks: {
}
}],
yAxes:
yAxisItems
}
}
}
setOptions(optionsTemp);
console.log("options: " , optionsTemp);
setdData(dDataTemp);
}}
else {
console.log("error in handleGetSelectedSensorData()");
}
}
And the Line is just added like this:
<Line data={dData} options = {options} />
Instead of setting display: true set display: 'auto', this will make the axis dissapear as long as there is no dataset visable that is linked to that scale, as soon as a dataset becomes visable that is linked to that scale it will show the scale again.
Doc: https://www.chartjs.org/docs/master/axes/cartesian/#common-options-to-all-axes
I use chart.js and I would like to display labels at the right of each bar but I got a problem with the first label.
I use the plugin chartjs-plugin-datalabels it work but the first label is hidden.
Do you know how to fix this?
https://jsfiddle.net/rq78pg4j/1/
var ctx4 = $('#chart_choice');
var chart_market = new Chart(ctx4, {
type: 'horizontalBar',
data: {
labels: ['Legend 1', 'Legend 2'],
datasets: [{
data: [12, 10],
label: '# of Votes',
backgroundColor: [
'rgba(243,212,205,1)',
'rgba(243,212,205,1)'
],
borderColor: [
'rgba(223,142,123,1)',
'rgba(223,142,123,1)'
],
borderWidth: 1
}]
},
options: {
plugins: {
datalabels: {
align: 'end',
anchor: 'end',
color: '#0B4892',
font: function(context) {
var w = context.chart.width;
return {
size: w < 512 ? 12 : 14,
weight: 'bold',
};
},
formatter: function(value, context) {
return context.chart.data.labels[context.dataIndex];
}
}
}
}
});
The problem is that Chart.js is not aware of what chartjs-plugin-datalabels is doing. Therefore, it doesn't reserve enough space at the right of the chart, allowing the labels of the largest bars to appear on the canvas.
This can be fixed by defining extra padding at the right of the chart as follows.
options: {
layout: {
padding: {
right: 100
}
},
...
For further details, please consult the page Layout Configuration from the Chart.js documentation.
Is there any way to expand the slice of donut chart onclick event or mouse hover in chartjs ?.
I am using chart.js#2.8.0 version.
I have fixed this issue using the following code:
let segment;
this.chart = new Chart(this.canvas, {
type: this.type,
data: this.data,
options: {
...this.options,
onHover: function (evt, elements) {
if (elements && elements.length) {
segment = elements[0];
this.chart.update();
selectedIndex = segment["_index"];
segment._model.outerRadius += 10;
} else {
if (segment) {
segment._model.outerRadius -= 10;
}
segment = null;
}
},
layout: {
padding: 30
}
}
});
I hope it helps you.
Shift-zooming the currently selected slice is not a feature but it has been discussed several times on various forums and the project's GitHub community:
https://forum.mendixcloud.com/link/questions/85582
https://github.com/chartjs/Chart.js/issues/1451
The Github discussion contains a fiddle someone wrote for a Pie chart with Chart.js 1.0. Here is an updated version for the current Chart.js version that supports Donut charts.
Code:
This part only shows the part that zooms the active element, it is just to give you the idea of how to use the active elements .innerRadius and .outerRadius properties to shift the element. The fiddle contains the complete code that also handles shrinking the previously selected element.
<div style="width:400px;">
<canvas id="myChart" width="200" height="200"></canvas>
</div>
var ctx = document.getElementById('myChart');
var myChart = new Chart(ctx, {
type: 'doughnut',
options: {
layout: {
padding: 30
}
},
data: {
labels: ['Red', 'Blue', 'Yellow'],
datasets: [
{
label: '# of Votes',
data: [4, 5, 3],
backgroundColor: [
'rgba(255, 0, 0, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)'
],
borderWidth: 3
}]
}
});
var addRadiusMargin = 10;
$('#myChart').on('click', function(event) {
var activePoints = myChart.getElementsAtEvent(event);
if (activePoints.length > 0) {
// update the newly selected piece
activePoints[0]['_model'].innerRadius = activePoints[0]['_model'].innerRadius +
addRadiusMargin;
activePoints[0]['_model'].outerRadius = activePoints[0]['_model'].outerRadius +
addRadiusMargin;
}
myChart.render(300, false);
}
Sample image:
Here is a sample of the highlighted slice:
Limitations:
There are two limitations of the sample that I haven't included:
Chart.js does not allow to define a margin between legend and chart content so when you are "zooming"/"moving", the zoomed slice might overlap parts of the legend. You may solve this by extending the legend as shown in Jordan Willis' Codepen which was the result of this SO question.
The selected slice will stay in contact with the remaining slices. If you want it to have a gap, you need to translate x and y of the active slice based on the .startAngle and .endAngleproperties of the active slice.
Another simple trick : hoverBorderWidth
var ctx = document.getElementById("chart").getContext('2d');
var chart = new Chart(ctx, {
type: 'pie',
data: {
labels: ["Microsoft Internet Explorer", "Google Chrome", "Mozilla Firefox", "Opera", "Safari"],
datasets: [{
label: 'Number of votes',
data: [60, 20, 10, 8, 5],
backgroundColor: ['#96ceff', '#424348', '#91ee7c', '#f7a35b', '#8286e9'],
borderColor: '#fff',
borderWidth: 1,
hoverBorderColor: ['#96ceff', '#424348', '#91ee7c', '#f7a35b', '#8286e9'],
hoverBorderWidth: 8
}]
},
options: {
responsive: false,
legend: {
display: false
},
tooltips: {
displayColors: false
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<canvas id="chart" width="350" height="350"></canvas>
I'm new in Chart JS and I have a bar chart with the legend displayed bellow the chart:
var data = {
labels: [],
datasets: [{
label: 'Disk C',
backgroundColor: "#000080",
data: [80]
}, {
label: 'Disk D',
backgroundColor: "#d3d3d3",
data: [90]
},
{
label: 'Memory',
backgroundColor: "#add8e6",
data: [45]
}]
};
var ctx = document.getElementById("mybarChart");
ctx.height = 300;
var mybarChart = new Chart(ctx, {
type: 'bar',
responsive: true,
data: data,
options: {
legend: {
display: true,
position: 'bottom'
},
scales: {
yAxes: [{
display: false,
ticks: {
beginAtZero: true
},
gridLines: {
color: "rgba(0, 0, 0, 0)",
}
}],
xAxes: [{
display: false,
gridLines: {
color: "rgba(0, 0, 0, 0)",
},
barPercentage: 0.5,
categoryPercentage: 0.5
}]
}
}
});
But the legend should be something like :
It is possible to make the colors rectangles much more smaller and the values to be displayed one below another because in my example the labels are displayed in a single row?
Unfortunately, there is no way to customize the default legend in the manner that you are wanting. Fortunately, however, chart.js thought of this and provided a mechanism for you to generate and style your own legend outside of the canvas object (using regular html/css).
You can use the legendCallback options property to define a method that generates your legend html and then call the chart .generateLegend() prototype method to place into your page. Here is what I mean.
HTML for my page.
<div style="width:25%;">
<canvas id="mybarChart"></canvas>
<div id="legend"></div>
</div>
Then I define how the legend will look in the 'legendCallback' option property.
legendCallback: function(chart) {
var text = [];
text.push('<ul class="' + chart.id + '-legend">');
for (var i = 0; i < chart.data.datasets.length; i++) {
text.push('<li><div class="legendValue"><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"> </span>');
if (chart.data.datasets[i].label) {
text.push('<span class="label">' + chart.data.datasets[i].label + '</span>');
}
text.push('</div></li><div class="clear"></div>');
}
text.push('</ul>');
return text.join('');
}
Finally, I add the legend html to my page.
$('#legend').prepend(mybarChart.generateLegend());
As always, here is a codepen example showing a working solution. You can change your legend look and feel simply by changing the html that is generated by the callback and using css.