I have a javascript map like this..
var Severity = {
3M:[0, 3, 1, 0, 0],
5T:[0, 0, 1, 0, 0],
6S:[0, 0, 2, 0, 0]
}
And a JS function to call Stacked Chart Bar. Here I have a created a JS function which takes id and a map from jsp page. Map structure is same as above defined. I want to display graph where in x axis the data is the keys in map and in y axes is the stacked up data of 5 elements.
function StackedBar(id,Severity) {
var label = Object.keys(Severity); // getting the labels
var Critical = [];
var High = [];
var Medium = [];
var Low = [];
var Others = [];
for(let i=0;i<label.length;i++){ //assigning the data to arrays created
Critical.push(Severity[label[i]][0]);
High.push(Severity[label[i]][1]);
Medium.push(Severity[label[i]][2]);
Low.push(Severity[label[i]][3]);
Others.push(Severity[label[i]][4]);
}
var ctxL = document.getElementById(id).getContext('2d'); //id from the html canvas
var chart = new Chart(ctxL, {
type: 'bar',
data: {
labels: label,
datasets: [
{
label: 'Critical',
data: Critical,
backgroundColor: '#aa000e'
},
{
label: 'High',
data: High,
backgroundColor: '#e65905'
},
{
label: 'Medium',
data: Medium,
backgroundColor: '#e00ce6'
},
{
label: 'Low',
data: Low,
backgroundColor: '#b8ab16'
},
{
label: 'Others',
data: Others,
backgroundColor: '#00aaaa'
}
]
},
options: {
responsive: true,
legend: {
position: 'right'
},
scales: {
xAxes: [{
stacked: true
}],
yAxes: [{
stacked: true
}]
}
}
});
}
Here graph displays and i get label in x axes...but graph values doesn't show and i get following error..
Html
<canvas id="overall"></canvas>
<script>StackedBar('overall',Overall);</script>
I wanted to know what went wrong and want me to help fix this issue...
I put the above together into one file and it works (although I had to change "Overall" to "Severity" in the call). So I'd expect that something you are using might not match your example above.
The version I used:
<html>
<body>
<canvas id="overall"></canvas>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script>
var Severity = {
"3M": [0, 3, 1, 0, 0],
"5T": [0, 0, 1, 0, 0],
"6S": [0, 0, 2, 0, 0]
};
</script>
<script>
function StackedBar(id, Severity) {
var label = Object.keys(Severity); // getting the labels
var Critical = [];
var High = [];
var Medium = [];
var Low = [];
var Others = [];
for (let i = 0; i < label.length; i++) { //assigning the data to arrays created
Critical.push(Severity[label[i]][0]);
High.push(Severity[label[i]][1]);
Medium.push(Severity[label[i]][2]);
Low.push(Severity[label[i]][3]);
Others.push(Severity[label[i]][4]);
}
var ctxL = document.getElementById(id).getContext('2d'); //id from the html canvas
var chart = new Chart(ctxL, {
type: 'bar',
data: {
labels: label,
datasets: [
{
label: 'Critical',
data: Critical,
backgroundColor: '#aa000e'
},
{
label: 'High',
data: High,
backgroundColor: '#e65905'
},
{
label: 'Medium',
data: Medium,
backgroundColor: '#e00ce6'
},
{
label: 'Low',
data: Low,
backgroundColor: '#b8ab16'
},
{
label: 'Others',
data: Others,
backgroundColor: '#00aaaa'
}
]
},
options: {
responsive: true,
legend: {
position: 'right'
},
scales: {
xAxes: [{
stacked: true
}],
yAxes: [{
stacked: true
}]
}
}
});
}
</script>
<script>StackedBar('overall', Severity);</script>
</html>
Related
I am displaying a bar chart that has 3 different pieces of information, (project name, number of days remaining, and the end date.) I am displaying the project name on one axis, and the number of days remaining determines the height of the bar. Currently, when I hover over a bar the tooltip displays the information already on the x and y axis. I want it to instead have the end date.
ie: project "b" will end in 2 days (August 4th), when I hover over the bar I want the tooltip to say "End date of 2022-08-04" instead of "b Work Days Remaining: 2"
My json of the data looks like this:
[{"po_num": "a", "days_rem": 10, "date_end": "2022-08-16"},
{"po_num": "b", "days_rem": 2, "date_end": "2022-08-04"},
{"po_num": "c", "days_rem": 6, "date_end": "2022-08-10"}]
Here is the link of the current graph.
https://i.stack.imgur.com/HefRz.png
Here is an MS paint rendering of what I am trying to do:
https://i.stack.imgur.com/GAT2I.png
The implementation code:
link = "{{{BASE_BACK_URL}}}";
$.getJSON(link, function (data) {
let po_names = [];
let days_rem = [];
for (let i = 0; i < data.length; i++) {
po_names.push(data[i]["po_num"]);
days_rem.push(data[i]["days_rem"]);
}
const ctx = document.getElementById('po-timeline-chart');
const myChart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: po_names,
datasets: [{
label: 'Work Days Remaining',
data: days_rem,
backgroundColor: 'rgb(0, 89, 178)'
}],
},
options: {
legend: {
align: "end"
},
scales: {
xAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
});
Solution listed below:
$.getJSON(link, function (data) {
let po_names = [];
let days_rem = [];
for (let i = 0; i < data.length; i++) {
po_names.push(data[i]["po_num"]);
days_rem.push(data[i]["days_rem"]);
}
const ctx = document.getElementById("po-timeline-chart");
const myChart = new Chart(ctx, {
type: "horizontalBar",
data: {
labels: po_names,
datasets: [
{
label: "Work Days Remaining",
data: days_rem,
backgroundColor: "rgb(0, 89, 178)",
},
],
},
options: {
tooltips: {
enabled: true,
callbacks: {
// To change title in tooltip
title: (data) => {
return "This PO will run out on";
},
// To change label in tooltip
label: (data) => {
return date_end[data['index']];
},
},
},
legend: {
align: "end",
},
scales: {
xAxes: [
{
ticks: {
beginAtZero: true,
},
},
],
},
},
});
});
I have the following JSON, that I want to insert a chart using Chart JS:
{"Results":[{"Data":"25/02/2021","Valor":18},{"Data":"24/02/2021","Valor":2993},{"Data":"23/02/2021","Valor":1936},{"Data":"22/02/2021","Valor":1844},{"Data":"21/02/2021","Valor":1114},{"Data":"20/02/2021","Valor":1060},{"Data":"19/02/2021","Valor":1134}]}
And I created a function to load this JSON into an Array:
function ShowData(jsonObj) {
var bases = jsonObj['Results'];
var Date = [];
var Val = [];
for (var i = bases.length-1; i >= 0; i--) {
Date.push([bases[i].Data]);
Val.push([bases[i].Valor]);
}
}
When I load this Array into the Chart, As below:
var chartGraph = new Chart(ctx,{
type:'line',
data:{
labels: Date,
datasets: [
{
label: "Lbl Name",
data: Val,
borderWidth: 6,
borderColor: 'rgba(77,166,253, 0.85)',
backgroundColor: 'transparent'
}
]
},
options: {
title: {
display: true,
fontSize: 20,
text: 'Chart Name'
},
legend: {
display: true,
position: 'right',
labels: {
fontColor: '#666'
}
}
}
})
No information on "datasets" appears to me, only the "label", what is the mistake I am making?
Graphic Image
Try to split series and data, something like:
function splitData(type) {
return json.Results.map(v => v[type]);
}
// your Chart.js config
data: {
labels: splitData('Date'),
datasets: [
{
// ...otherProps,
data: splitData('Valor')
}
]
}
You cant use Date as variable name since its a build in class. Also from my testing couldnt reference the vars inside the function. But the real problem with your code is that you push an array to the val array. This made it an array containing arrays. This is not supported. If you change your code to the sample below it will work
let date = [];
let val = [];
function ShowData(jsonObj) {
var bases = jsonObj['Results'];
date = [];
val = [];
for (var i = bases.length-1; i >= 0; i--) {
date.push(bases[i].Data);
val.push(bases[i].Valor);
}
}
var chartGraph = new Chart(ctx,{
type:'line',
data:{
labels: Date,
datasets: [
{
label: "Lbl Name",
data: Val,
borderWidth: 6,
borderColor: 'rgba(77,166,253, 0.85)',
backgroundColor: 'transparent'
}
]
},
options: {
title: {
display: true,
fontSize: 20,
text: 'Chart Name'
},
legend: {
display: true,
position: 'right',
labels: {
fontColor: '#666'
}
}
}
})
I have graph that I built with the Chart.js library :
Normally, Sshare is represented with two color, red and green. In the legend, however, Sshare displays with just the first color value, red.
How can I get both Sshare colors to show in the legend?
I tried searching for a solution in the Chart.js documentation, but could not find a way to edit the legend properties.
My code:
<script>
// chart colors //BLUE & RED & VERT
var colors = ['#007bff','#dc3545',"#008000"];
var colors_suggested =[];
var labels_in =[];
var mshare_value =[];
var svalues =[];
var data_work_in =[
{ "ID":12, "Les": "AB", "Name": " AB_12", "Mmin": 75, "Sshare": 29},
{ "ID":13, "Les": "BB", "Name": " BB_13", "Mmin": 26.8, "Sshare": 36}
];
for (var i = 0; i < data_work_in.length;i++)
{
labels_in.push(data_work_in[i].Name+";"+data_work_in[i].Mill);
mshare_value.push(data_work_in[i].Mmin);
svalues.push(data_work_in[i].Sshare);
if ( data_work_in[i].Sshare >= data_work_in[i].Mmin)
colors_suggested.push(colors[2]);
else {
colors_suggested.push(colors[1]);
}
}
var chBar = document.getElementById("chBar");
var chartData = {
// Label of Entity
labels: labels_in,
// Value of percent category RTI|| VSM ...
datasets: [{
label: 'Mmin',
data: mshare_value,
backgroundColor: colors[0]
},
{
label: 'Sshare',
data: svalues,
xAxisID:'xAxis1',
backgroundColor: colors_suggested
}]
};
if (chBar) {
// new graph
new Chart(chBar, {
type: 'bar',
data: chartData,
options: {
scales: {
xAxes:[
{
barPercentage: 0.9,
categoryPercentage: 0.7,
id:'xAxis1',
type:"category",
ticks:{
callback:function(label){
var sublabel_x = label.split(";")[0];
var label_p = label.split(";")[1];
return sublabel_x;
}
}
},
{
id:'xAxis2',
type:"category",
gridLines: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
ticks:{
callback:function(label){
var sublabel_x = label.split(";")[0];
var label_p = label.split(";")[1];
return label_p;
}
}
}],
yAxes: [{
ticks: {
beginAtZero: false
},
scaleLabel: {
display: true,
labelString: '%'
}
}]
},
legend: {
display: true,
legendText : ['Mmin','Sshare']
}
}
});
}
</script>
Following is my javascript Code, but the only thing really relevant is the last function. I want to update the Chart to add another dataset, without reloading the Page. But for reason the added dataset is always undefined. The commented-out line, which uses the exact same array of data, on the other hand works. Since I'm new to javascript I'm not sure, if I missed something obvious, or if chart.js just doesn't support this kind of thing at all.
const CHART = document.getElementById("lineChart");
var dts1 = [
{
label: "Abfall gesamt",
data: Abfall_gesamt,
}
];
var dts2 = [
{
label: "Abfall schadstoffhaltiger",
data: Abfall_schadstoff,
}
];
var lineChart = new Chart(CHART, {
type: 'line',
data: {
labels: Jahr,
datasets: dts1
}
});
function myFunction(){
//lineChart.data.datasets[0].data = Abfall_schadstoff;
lineChart.data.datasets.push(dts2);
lineChart.update();
}
The issue is, you are defining your datasets (dts1 and dts2) as an array. They should be an object, like so ...
var dts1 = {
label: "Abfall gesamt",
data: Abfall_gesamt,
};
var dts2 = {
label: "Abfall schadstoffhaltiger",
data: Abfall_schadstoff,
};
and then, when generating the chart, set datasets value as ...
datasets: [dts1]
ᴅᴇᴍᴏ
const CHART = document.getElementById("lineChart");
var Abfall_gesamt = [1, 2, 3];
var Abfall_schadstoff = [4, 5, 6]
var dts1 = {
label: "Abfall gesamt",
data: Abfall_gesamt,
backgroundColor: 'rgba(255, 0, 0, 0.2)'
};
var dts2 = {
label: "Abfall schadstoffhaltiger",
data: Abfall_schadstoff,
backgroundColor: 'rgba(0, 0, 255, 0.2)'
};
var lineChart = new Chart(CHART, {
type: 'line',
data: {
labels: ['Jahr', 'Mahr', 'Kadr'],
datasets: [dts1]
},
options: {
responsive: false,
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
stepSize: 1
}
}]
}
}
});
function myFunction() {
//lineChart.data.datasets[0].data = Abfall_schadstoff;
lineChart.data.datasets.push(dts2);
lineChart.update();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<button id="add" onclick="myFunction()">Add Dataset</button>
<canvas id="lineChart" height="190"></canvas>
I'm using chart.js (V2) to try to build a bar chart that has more information available to user without having to hover over or click anywhere. I've provided two examples of how I hope to edit my chart.
Two edited versions of what I hope to achieve
As can be seen, I hope to place (somewhere), some extra information outside of the labels. I had hope that by adding '\n' to the labels I might have been able to get what I was looking for similar to option A.
Some edited code is provided blow:
var barChartData = {
labels: playerNames,
datasets: [{
label: 'Actual Score/Hour',
backgroundColor: "rgba(0, 128, 0,0.5)",
data: playerScores
}, {
label: 'Expected Score/Hour',
backgroundColor: "rgba(255,0,0,0.5)",
data: playerExpected
}]
};
function open_win(linktosite) {
window.open(linktosite)
}
canvas.onclick = function(evt){
var activePoints = myBar.getElementsAtEvent(evt);
console.log(activePoints);
linktosite = 'https://www.mytestsite.com/' + activePoints[1]['_model']['label'];
open_win(linktosite);
};
window.onload = function() {
var ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx, {
type: 'bar',
data: barChartData,
options: {
title:{
display:true,
text:"Player Expected and Actual Score per Hour"
},
tooltips: {
mode: 'label'
},
responsive: true,
scales: {
xAxes: [{
stacked: false,
}],
yAxes: [{
stacked: false
}]
},
animation: {
onComplete: function () {
var ctx = this.chart.ctx;
ctx.textAlign = "center";
Chart.helpers.each(this.data.datasets.forEach(function (dataset) {
Chart.helpers.each(dataset.metaData.forEach(function (bar, index) {
// console.log("printing bar" + bar);
ctx.fillText(dataset.data[index], bar._model.x, bar._model.y - 10);
}),this)
}),this);
}
}
}
});
// Chart.helpers.each(myBar.getDatasetMeta(0).data, function(rectangle, index) {
// rectangle.draw = function() {
// myBar.chart.ctx.setLineDash([5, 5]);
// Chart.elements.Rectangle.prototype.draw.apply(this, arguments);
// }
// }, null);
};
At this point I'd be satisfied with having the extradata anywhere on the bar. Any help would be appreciated. Thanks~
Chart.js v2.1.5 allows for multi-line labels using nested arrays (v2.5.0 fixes it for radar graphs):
...
data: {
labels: [["Jake", "Active: 2 hrs", "Score: 1", "Expected: 127", "Attempts: 4"],
["Matt", "Active: 2 hrs", "Score: 4", "Expected: 36", "Attempts: 4"]],
...
However, this does mean that you will have to pre-calculate the label values.
var config = {
type: 'line',
data: {
labels: [["January","First Month","Jellyfish","30 of them"], ["February","Second Month","Foxes","20 of them"], ["March","Third Month","Mosquitoes","None of them"], "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
data: [65, 40, 80, 81, 56, 85, 45],
backgroundColor: "rgba(255,99,132,0.2)",
}, {
label: "My Second dataset",
data: [40, 80, 21, 56, 85, 45, 65],
backgroundColor: "rgba(99,255,132,0.2)",
}]
},
scales : {
xAxes : [{
gridLines : {
display : false,
lineWidth: 1,
zeroLineWidth: 1,
zeroLineColor: '#666666',
drawTicks: false
},
ticks: {
display:true,
stepSize: 0,
min: 0,
autoSkip: false,
fontSize: 11,
padding: 12
}
}],
yAxes: [{
ticks: {
padding: 5
},
gridLines : {
display : true,
lineWidth: 1,
zeroLineWidth: 2,
zeroLineColor: '#666666'
}
}]
},
spanGaps: true,
responsive: true,
maintainAspectRatio: true
};
var ctx = document.getElementById("myChart").getContext("2d");
new Chart(ctx, config);
<div class="myChart">
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js"></script>
<canvas id="myChart"></canvas>
</div>
If a label is an array as opposed to a string i.e. [["June","2015"], "July"] then each element is treated as a separate line. The appropriate calculations are made to determine the correct height and width, and rotation is still supported.
charJS version 2.7.2 used
this also works in https://github.com/jtblin/angular-chart.js
If you are using Chart.js v2.7.1, the above solution might not work.
The solution that actually worked for us was adding a small plugin right in the data and options level:
const config = {
type: 'bar',
data: {
// ...
},
options: {
// ...
},
plugins: [{
beforeInit: function (chart) {
chart.data.labels.forEach(function (label, index, labelsArr) {
if (/\n/.test(label)) {
labelsArr[index] = label.split(/\n/)
}
})
}
}]
};
A full description of how to fix this issue can be found here.
With Chart.js v2.1, you can write a chart plugin to do this
Preview
Script
Chart.pluginService.register({
beforeInit: function (chart) {
var hasWrappedTicks = chart.config.data.labels.some(function (label) {
return label.indexOf('\n') !== -1;
});
if (hasWrappedTicks) {
// figure out how many lines we need - use fontsize as the height of one line
var tickFontSize = Chart.helpers.getValueOrDefault(chart.options.scales.xAxes[0].ticks.fontSize, Chart.defaults.global.defaultFontSize);
var maxLines = chart.config.data.labels.reduce(function (maxLines, label) {
return Math.max(maxLines, label.split('\n').length);
}, 0);
var height = (tickFontSize + 2) * maxLines + (chart.options.scales.xAxes[0].ticks.padding || 0);
// insert a dummy box at the bottom - to reserve space for the labels
Chart.layoutService.addBox(chart, {
draw: Chart.helpers.noop,
isHorizontal: function () {
return true;
},
update: function () {
return {
height: this.height
};
},
height: height,
options: {
position: 'bottom',
fullWidth: 1,
}
});
// turn off x axis ticks since we are managing it ourselves
chart.options = Chart.helpers.configMerge(chart.options, {
scales: {
xAxes: [{
ticks: {
display: false,
// set the fontSize to 0 so that extra labels are not forced on the right side
fontSize: 0
}
}]
}
});
chart.hasWrappedTicks = {
tickFontSize: tickFontSize
};
}
},
afterDraw: function (chart) {
if (chart.hasWrappedTicks) {
// draw the labels and we are done!
chart.chart.ctx.save();
var tickFontSize = chart.hasWrappedTicks.tickFontSize;
var tickFontStyle = Chart.helpers.getValueOrDefault(chart.options.scales.xAxes[0].ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
var tickFontFamily = Chart.helpers.getValueOrDefault(chart.options.scales.xAxes[0].ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
var tickLabelFont = Chart.helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
chart.chart.ctx.font = tickLabelFont;
chart.chart.ctx.textAlign = 'center';
var tickFontColor = Chart.helpers.getValueOrDefault(chart.options.scales.xAxes[0].fontColor, Chart.defaults.global.defaultFontColor);
chart.chart.ctx.fillStyle = tickFontColor;
var meta = chart.getDatasetMeta(0);
var xScale = chart.scales[meta.xAxisID];
var yScale = chart.scales[meta.yAxisID];
chart.config.data.labels.forEach(function (label, i) {
label.split('\n').forEach(function (line, j) {
chart.chart.ctx.fillText(line, xScale.getPixelForTick(i + 0.5), (chart.options.scales.xAxes[0].ticks.padding || 0) + yScale.getPixelForValue(yScale.min) +
// move j lines down
j * (chart.hasWrappedTicks.tickFontSize + 2));
});
});
chart.chart.ctx.restore();
}
}
});
and then
...
data: {
labels: ["January\nFirst Month\nJellyfish\n30 of them", "February\nSecond Month\nFoxes\n20 of them", "March\nThird Month\nMosquitoes\nNone of them", "April", "May", "June", "July"],
...
Note - we assume that the maximum content of one line will fit between the ticks (i.e. that no rotation logic is needed. I'm sure it's possible to incorporate rotation logic too, but it would be a tad more complicated)
You should format the tooltips to not show the x axis label, or format it to show a shorter version of the label.
Fiddle - http://jsfiddle.net/m0q03wpy/