It seems like this is a common issue with charts.js. I created a site that allows you to choose a date. When you click a specific date a line chart updates given that dates' csv file. If you then go and click on a new date the chart.js line chart updates but if you over over the data it briefly displays the previous dates data.
I have seen a bunch of questions on this before and have tried to use the .destroy() along with updating the chart but every time I add this to any point of the code, the canvas simply gets destroyed and a chart is never created.
HTML:
<div class="chooseGame">
<button onclick="dropDownFunction()" class="gameDropDown">Select a Game Date & Time</button>
<div id="dropdown" class="dates"></div>
</div>
JS:
function dropDownFunction() {
document.getElementById("dropdown").classList.toggle("show");
}
window.onclick =
function(event) {
if (!event.target.matches('.gameDropDown')) {
var dropdowns = document.getElementsByClassName("dates");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
$(document).on("click", ".test",function(e){
var selectedDate= ($(this).text())
document.getElementById("today").innerText = selectedDate;
var cFD = document.getElementById('Canvas').getContext('2d');
var backgroundFD = document.getElementById('fDChart').style.background='white';
FirstData = new Chart(cFD, {
type: 'line',
data: {
labels: fdgameArray,
datasets: [{
label: 'New',
backgroundColor: "#f15a22",
borderColor: "#f15a22",
data: fdNewData,
fill: false,
},
{
label: 'Completed',
backgroundColor: "#004684",
borderColor: "#004684",
data: fdCompData,
fill: false,
},
{
label: 'Oustanding',
fontColor: '#a7b1c2',
borderColor: '#a7b1c2',
data: fdOutData,
fill: false,
}]
},
options: {
responsive: true,
title:{
display:false,
text: 'Delta Club',
fontSize: 15,
fontFamily: 'Arial',
fontColor: '#004684'
},
tooltips:{
mode: "index",
intersect: false,
},
hover:{
mode:"nearest",
intersect: true
},
scales:{
xAxes:[{
display: true,
ticks:{
fontColor:'#004684',
fontSize: 10
},
scaleLabel:{
display: true,
labelString: "Time",
fontColor:'#004684',
fontSize: 12
}
}],
yAxes:[{
display: true,
ticks:{
fontColor:'#004684',
fontSize: 10,
beginAtZero: true,
steps: 10,
stepValue: 1,
Max: 100
},
scaleLabel:{
display: true,
labelString: "# Orders",
fontColor:'#004684',
fontSize: 12
}
}]
}
}})
};
Whenever you change the date and hover over the chart, the previous date's data is displayed and then reverts back to current selected date. I want current data displayed always.
I actually was able to adjust the following and now I am all set:
var cFD = document.getElementById('Canvas').getContext('2d');
var backgroundFD = document.getElementById('firstDataChart').style.background='white';
if(window.chart && window.chart !== null){
window.chart.destroy();
}
window.chart = new Chart(cFD, {...})
I use CanvasJS. Whenever the chart contains multiple dataSeries, it is recommended to represent each dataSeries in a legend(in jsfiddel ANS1,ANS2 is legend name) and hovers on a dataPoint or dataSeries, a toolTip appears with information about the dataPoint and dataSeries. when i click legend each then hide the data set and remove graph bar but not on hover tooltip.
1. 1st Image is correct because 2 tooltip and 2 dataset
when i click ANC1 it hide but tooltip still show.
2. 2nd Image is incorrect because still 2 tooltip and 1 dataset
My Code in
jsfiddle
var chart = new CanvasJS.Chart("chartContainer",
{
title:{
text: "title"
},
axisX:{
title: "xxxxxxxxxx",
labelAngle: 135,
labelFontSize: 23
},
axisY:{
title:"Number of Services"
},
axisY2: {
title: "Number of Eligible Couple",
titleFontSize: 25,
},
toolTip: {
enabled: true,
shared: true },
animationEnabled: true,
legend:{
horizontalAlign: "center", // left, center ,right
verticalAlign: "top", // top, center, bottom
cursor:"pointer",
itemclick: function(e) {
if (typeof (e.dataSeries.visible) === "undefined" || e.dataSeries.visible) {
e.dataSeries.visible = false;
}
else
{
e.dataSeries.visible = true;
}
chart.render();
}
},
data: [
{
type: "column",
name: "ANC1",
showInLegend: "true",
visible: true,
dataPoints: [{label:"ROY2",x:0,y:36},{label:"ROY3",x:1,y:36}]
},
{
type: "column",
name: "ANC2",
showInLegend: "true",
visible: true,
dataPoints: [{label:"ROY2",x:0,y:56},{label:"ROY3",x:1,y:36}]
}
]
});
chart.render();
You can set up a custom tooltip https://canvasjs.com/docs/charts/chart-options/tooltip/content-formatter/
Code for your case:
var chart = new CanvasJS.Chart("chartContainer",
{
toolTip: {
enabled: true,
shared: true,
contentFormatter: function(e){
var str = "";
for (var i = 0; i < e.entries.length; i++){
if(e.entries[i].dataSeries.visible){
var temp = (e.entries[i].dataSeries.name + ': '+ e.entries[i].dataPoint.y);
str = str.concat(temp, '<br>');
}
};
return (str);
}
},
...
I am trying to implement an Angular horizontal stacked bar chart- Something like this example.
However, I want just one bar that is stacked.
I am working with AngularJS and Chart.js. I have the example showing on the page.
In the PieController, ChartData contains:
{"data":["63","38"],"labels":["Ford","GM"]}
In the example, instead of the label on the outside, I would like the label and then the number to be inside the chart. Like [=====Ford 63====|===GM 38===] the equals represent bar colors. There will be more data points than the current two.
Here is my page
<div ng-controller="PieController">
data {{ChartData}} //testing purposes
<div ng-init="getBarChart()">
<canvas id="Chart1"></canvas>
</div>
Here is my JavaScript controller
app.controller('PieController', function($scope, ChartService) {
$scope.getBarChart = function(){
ChartService.get({name: 'main'}, function(data) {
$scope.ChartData = data;
var barOptions_stacked = {
tooltips: {
enabled: false
},
hover: {
animationDuration: 0
},
scales: {
xAxes: [{
ticks: {
beginAtZero: true,
fontFamily: "'Open Sans Bold', sans-serif",
fontSize: 11
},
scaleLabel: {
display: false
},
gridLines: {},
stacked: true
}],
yAxes: [{
gridLines: {
display: false,
color: "#fff",
zeroLineColor: "#fff",
zeroLineWidth: 0
},
ticks: {
fontFamily: "'Open Sans Bold', sans-serif",
fontSize: 11
},
stacked: true
}]
},
legend: {
display: false
},
animation: {
onComplete: function () {
var chartInstance = this.chart;
var ctx = chartInstance.ctx;
ctx.textAlign = "left";
ctx.font = "9px Open Sans";
ctx.fillStyle = "#fff";
Chart.helpers.each(this.data.datasets.forEach(function (dataset, i) {
var meta = chartInstance.controller.getDatasetMeta(i);
Chart.helpers.each(meta.data.forEach(function (bar, index) {
data = dataset.data[index];
if (i == 0) {
ctx.fillText(data, 50, bar._model.y + 4);
} else {
ctx.fillText(data, bar._model.x - 25, bar._model.y + 4);
}
}), this)
}), this);
}
},
pointLabelFontFamily: "Quadon Extra Bold",
scaleFontFamily: "Quadon Extra Bold",
};
var ctx = document.getElementById("Chart1");
var myChart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: ["2014", "2013", "2012", "2011"],
datasets: [{
data: [727, 589, 537, 543, 574],
backgroundColor: "rgba(63,103,126,1)",
hoverBackgroundColor: "rgba(50,90,100,1)"
}, {
data: [238, 553, 746, 884, 903],
backgroundColor: "rgba(163,103,126,1)",
hoverBackgroundColor: "rgba(140,85,100,1)"
}, {
data: [1238, 553, 746, 884, 903],
backgroundColor: "rgba(63,203,226,1)",
hoverBackgroundColor: "rgba(46,185,235,1)"
}]
},
options: barOptions_stacked,
})
});//end chat service.get
}
});
Instead of hard coding in the dataset which is what its doing now, is there a way I can use the data "ChartData" that I outlined in the beginning of the post?
I'm not sure how to do it or if its even possible.
In order to get the label to show in the bar, reference the labels property of $scope.ChartData:
ctx.fillText($scope.ChartData.labels[index] +" "+ data, 50, bar._model.y + 4);
Instead of hard coding in the dataset which is what its doing now, is there a way I can use the data "ChartData" that I outlined in the beginning of the post?
Use properties from the data variable (supplied by the ChartService get function) i.e. data.labels and data.data instead of the hard-coded values.
So update the labels line from:
labels: ["2014", "2013", "2012", "2011"],
To this:
labels: data.labels,
And similarly for the datasets:
datasets: [{
data: data.data,
So when creating the Chart it should look like this:
var myChart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: data.labels,
datasets: [{
data: data.data,
backgroundColor: "rgba(63,103,126,1)",
hoverBackgroundColor: "rgba(50,90,100,1)"
}]
},
options: barOptions_stacked,
});
Expand the code snippet below for a demonstration.
Note: there is currently an issue with the resize-listener, blocked by a CORS issue - I am trying to find a way to disable that.
Update:
Per your comment about stacking the bars (horizontally) - yes that is possible. Just have one element in the datasets array for each item. One simple way to have this is to use Array.map() to create an array similar to the example:
var bgColors = [
"rgba(63,103,126,1)",
"rgba(50,90,100,1)"
];
var hoverBgColors = [
"rgba(50,90,100,1)",
"rgba(140,85,100,1)"
];
var datasets = data.data.map(function(value, index) {
return {
data: [value],
backgroundColor: bgColors[index],
hoverBackgroundColor: hoverBgColors[index]
}
});
Then use variable datasets when creating the Chart object:
var myChart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: data.labels,
datasets: datasets
},
options: barOptions_stacked,
});
Also, there is some weird math going on for the location of the second label but the helper function can be updated like below (I tried dividing the x value by 75 but that may need to be adjusted - I am not sure what "appropriate" values are for that ...):
if (i == 0) {
ctx.fillText($scope.ChartData.labels[i] + " " + data, 50, bar._model.y + 4);
} else {
ctx.fillText($scope.ChartData.labels[i] + " " + data, (bar._model.x - 25) / 75, bar._model.y + 4);
}
var app = angular.module('myApp', []);
app.factory('ChartService', function() {
return { //dummy chart service
get: function(obj, callback) {
var data = {
"data": ["63", "38"],
"labels": ["Ford", "GM"]
};
callback(data);
}
};
});
app.controller('PieController', function($scope, ChartService) {
$scope.getBarChart = function() {
ChartService.get({
name: 'main'
}, function(data) {
$scope.ChartData = data;
var barOptions_stacked = {
tooltips: {
enabled: false
},
hover: {
animationDuration: 0
},
scales: {
xAxes: [{
ticks: {
beginAtZero: true,
fontFamily: "'Open Sans Bold', sans-serif",
fontSize: 11
},
scaleLabel: {
display: false
},
gridLines: {},
stacked: true
}],
yAxes: [{
gridLines: {
display: false,
color: "#fff",
zeroLineColor: "#fff",
zeroLineWidth: 0
},
ticks: {
fontFamily: "'Open Sans Bold', sans-serif",
fontSize: 11
},
stacked: true
}]
},
legend: {
display: false
},
animation: {
onComplete: function() {
var chartInstance = this.chart;
var ctx = chartInstance.ctx;
ctx.textAlign = "left";
ctx.font = "9px Open Sans";
ctx.fillStyle = "#fff";
Chart.helpers.each(this.data.datasets.forEach(function(dataset, i) {
var meta = chartInstance.controller.getDatasetMeta(i);
Chart.helpers.each(meta.data.forEach(function(bar, index) {
data = dataset.data[index];
if (i == 0) {
ctx.fillText($scope.ChartData.labels[i] + " " + data, 50, bar._model.y + 4);
} else {
ctx.fillText($scope.ChartData.labels[i] + " " + data, (bar._model.x - 25) / 75, bar._model.y + 4);
}
}), this)
}), this);
}
},
pointLabelFontFamily: "Quadon Extra Bold",
scaleFontFamily: "Quadon Extra Bold",
};
var ctx = document.getElementById("Chart1");
var bgColors = [
"rgba(63,103,126,1)",
"rgba(50,90,100,1)"
];
var hoverBgColors = [
"rgba(50,90,100,1)",
"rgba(140,85,100,1)"
];
var datasets = data.data.map(function(value, index) {
return {
data: [value],
backgroundColor: bgColors[index],
hoverBackgroundColor: hoverBgColors[index]
}
});
var myChart = new Chart(ctx, {
type: 'horizontalBar',
data: {
//use empty labels because the labels are on the bars
labels: data.labels.map(function() {
return '';
}),
datasets: datasets
},
options: barOptions_stacked,
})
}); //end chat service.get
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.3/Chart.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="PieController">
data {{ChartData}} //testing purposes
<div ng-init="getBarChart()">
<canvas id="Chart1"></canvas>
I am forming the kendo char chart in the below way. In javascript getKendoChart is coming as undefined. Could you please check and let me know how to resolve this.
var rdChartBy = '#Model.paretoFilters.ReportBy';
if(rdChartBy == 'Project Submitted')
$("#rbnprojSub").prop("checked", true);
if(rdChartBy == 'FA Completed')
$("#rbnprojComp").prop("checked", true);
var paretoData = #Html.Raw(Json.Encode(#Model.paretoChartData));
var dsParetoData = new kendo.data.DataSource({
data: paretoData
});
$("#ParetoChart").kendoChart({
title: {
text: "Pareto Chart Report",
font: "bold 20px Arial,Helvetica,sans-serif",
color: "brown"
},
dataSource: dsParetoData,
series: [{
type: "column",
categoryField: "Month",
name : "No.Of Projects",
field:"No_Of_Projects"
},{
type:"line",
categoryField:"Month",
style: "rigid",
name:"Cumilative%",
field:"Cumulative"
}],
valueAxis: {
title: {
text: "No.OfProjects/Cumilative%",
font: "bold 15px Arial,Helvetica,sans-serif",
color: "brown"
}
},
chartArea: {
width: 850,
height: 400
},
categoryAxis:{
title: {
text: "Month",
font: "bold 18px Arial,Helvetica,sans-serif",
color: "brown"
},
labels: { rotation: -45 },
width:50
},
tooltip: {
visible: true,
template: "${series.name} : ${value}"
}
});
function exportChart(e)
{
debugger;
var chart = $("#ParetoChart").getKendoChart();
chart.exportImage().done(function (data) {
kendo.saveAs({
dataURI: data,
fileName: "chart.png",
proxyURL: "#Url.Action("Export_Save", "Chart_Api")"
});
});
Try replacing
var chart = $("#ParetoChart").getKendoChart();
with
var chart = $("#ParetoChart").data("kendoChart");
I am trying to use javascript so that when the chart is sized below a certain threshold, the datalabels are hidden. I have the dataLabels initiated in an array of series'. Here's a snippet from the array.
{
name: 'Muni floaters',
color: '#009fdf',
data: [355.000],
id: 'columnText',
dataLabels: {
enabled: true,
inside: true,
//align: 'right',
x: 135,
format: '<b>{series.name}</b>',
color: '#009fdf',
style: {
fontSize: '12px'
}
}
}, {
name: 'Bank loans',
color: '#004b87',
data: [622.955],
id: 'columnText',
dataLabels: {
enabled: true,
inside: true,
//align: 'left',
x: -130,
format: '<b>{series.name}</b>',
color: '#004b87',
style: {
fontSize: '12px',
}
}
}
I tried the following javascript to disable the series dataLabels but only received errors.
function responsiveText(){
if ($('#container').width() < 578){
var chart = $('#container').highcharts();
var options = chart.options;
options.series.dataLabels.enabled = false;
chart = new Highcharts.Chart(options);
}
else{
var chart = $('#container').highcharts();
var options = chart.options;
options.series.dataLabels.enabled = true;
chart = new Highcharts.Chart(options);
}
}
$( window ).resize(function() {
responsiveText();
});
Any Ideas?
Do not attempt to re-init the chart with:
chart = new Highcharts.Chart(options);
Instead use the series update method.
function responsiveText(){
Highcharts.charts[0].series[0].update({
dataLabels: {
enabled: !($('#container').width() < 578)
}
}, true);
}
Fiddle demo here.