ChartJs mouse hover bug (showing previous charts) - javascript

I think my issue is a known one, everytime I make a new chart and hover my mouse over some points in the canvas, the old charts flash in and out all the time. Here's one of the threads I followed to try and fix it, but nothing seems to work. I tried re-appending the canvas, using destroy, clear, if statements which should clear it, but nothing.
Here's my code:
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: chartjsDate,
datasets: [{
label: 'temp',
data: chartjsTemp,
backgroundColor: "rgba(240,240,240,0.5)"
}]
}
});
I tried adding myChart.destroy(); before and after that code, even if(myChart!=null){myChart.destroy();}, but still nothing. Any help on how to fix it would be appreciated. All other threads I found are quite old and their solutions don't work.
Edit: Some stuff I tried, to no avail:
var myChart;
if (myChart != undefined || myChart !=null) {
myChart.destroy();
}

I also had this problem. To solve it,
you first have to declare myChart variable globally and then try this way.
//var my chart declare globally.
let chartData = {
type: 'line',
data: {
labels: chartjsDate,
datasets: [{
label: 'temp',
data: chartjsTemp,
backgroundColor: "rgba(240,240,240,0.5)"
}]
}
if (typeof(this.myChart) != "undefined") {
this.myChart.destroy();
}
const ctx = this.renderRoot.querySelector('#chart-canvas').getContext('2d');
this.myChart = new Chart(ctx, chartData);
this.myChart.update();

Solved! I added this above the javascript code:
var button = document.getElementById("submitButton");
submitButton.addEventListener("click", function(){
myChart.destroy();
});
And changed my submit button to have the id "submitButton", like this:
<input type="submit" class="btn btn-danger" id="submitButton" value="Send" />
This way, everytime you press the submit button, it destroys the previous chart. The weird thing is that when trying to use myChart.destroy(); I got errors.

Above solutions are working for me but when I have two charts on the same page, Only one is showing. Other charts are becoming empty. Here is the solution for that.
var ctx = document.getElementById("bar-chart").getContext("2d");
//Destroy the previous chart;
//Rename the "bar" according to your component
if(window.bar != undefined)
window.bar.destroy();
window.bar = new Chart(ctx , {});
if you don't change the "bar", only one chart will show in your page.

It is not bug. Basically you were creating a new graph each time. What you need here is to update graph instead of drawing a new one on that canvas. This can be done using the following code.
if(typeof Graph ==="undefined"){
window.Graph = new Chart(Graph_ctx, Graph_config);
}else{
//updating with new chart data
window.Graph.config=Graph_config;
//redraw the chart
window.Graph.update();
}

var ctxLine = document.getElementById("line-chart").getContext("2d");
//var myLineChart;
//myLineChart;
if(window.bar != undefined)
window.bar.destroy();
window.bar = new Chart(ctxLine, {});
For more clear check this:
solved-hovering-chartjs-bar-chart-showing-old-data

I usually do this before creating my chart
document.querySelector(".chart-container").innerHTML= '<canvas id="bar_chart"></canvas>';
var chart3 = $('#bar_chart');
graph = new Chart(chart3, {
type: 'bar',
data: chart_data,
options: options
});
My html:
<div class="chart-container">
<canvas id="bar_chart"></canvas>0
</div>

Just Use That and solve your Problem ;
if(window.myChart!=undefined) {
var oldcanvasid= window.myChart.chart.ctx.canvas.id;
if(chardcanvasid==oldcanvasid) {
window.myChart.destroy();
}
}
var ctx = document.getElementById(chardcanvasid).getContext('2d');
window.myChart = new Chart(ctx, {
type: charttype,
data: datass,
options: optionss,
});

Related

CanvasJS chart not showing

I am trying to create a bar chart with data for years 2009 to 2015 of enrolled students in given majors at universities in Mauritius. I am using the Javascript below and console.log(data) shows that the chart has been rendered. However, it does not display.
var dataPoints = [];
var myChart = document.getElementById('myChart').getContext('2d');
$('#all').click(function(){
$.get("http://api.worldbank.org/v2/countries/mus/indicators/SE.TER.ENRL?date=2009:2015", function(data) {
console.log(data);
$(data).find("wb:data").each(function () {
var $dataPoint = $(this);
var x = $dataPoint.find("wb:date").text;
console.log(x);
var y = $dataPoint.find("wb:value").text();
dataPoints.push({x: parseFloat(x), y: parseFloat(y)});
});
var chart = new CanvasJS.Chart("myChart", {
title: {
text: $(data).find("wb:indicator").text(),
},
data: [{
type: "column",
dataPoints: dataPoints,
}]
});
chart.render();
document.getElementById('myChart').style.display = "block";
});
});
This is the html. The canvas is initially set to display:none.
<canvas id="myChart">
</canvas>
Chart container should be a DOM and not Canvas. Please take a look at CanvasJS Documentation Page for more info. Changing <canvas id="myChart"> </canvas> to <div id="myChart"></div> and passing it to chart while creating it should work fine in your case.
You can also refer tutorial on Rendering Chart from XML Data from docs.
There are two different charting javascripts
chart.js
canvasjs
When someone transit from 1 to 2, this mistake occur.
I had difficulty using chart.js offline
So, I started with canvasjs
causing the mistake.

How can I load multiple Chartjs charts with different data on the same page?

I have a website and some charts built with chart js. The data for the charts is all in one file, births.js. Before, there were multiple pages, so to load the correct chart I used a function and if statement for each page
function drawChart() {
if (window.location.href.indexOf("scotland") > -1) {
}
else if(window.location.href.indexOf("index") > -1){
}
etc.
I've now been tasked with having everything on one interactive page, so the indexOf will always just be index. Is there a way to rewrite the function so that I can load the different data on the same page?
well, then you can use different ids for different charts.not even this every jquery or js plugin can be used like this.
<canvas id="yourid1" width="400" height="400"></canvas><canvas id="yourid2" width="400" height="400"></canvas>
var ctx = document.getElementById("yourid2");
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels:......///and so on for your another charts
var ctx = document.getElementById("yourid1");
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels:.......///not the complete code. just reference
same again with another id for different charts
<canvas id="yourid2" width="400" height="400"></canvas>
var ctx = document.getElementById("yourid2");
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels:......///and so on for your another charts

Click every label onload ChartJS

I'm trying to click every label after the chart is loaded using a ChartJS chart. I've written the following code and am having trouble getting past what I've got. Any insight or help would be appreciated.
myChart.legend.legendItems.forEach(function(label, key) {
console.log(myChart.legend.legendItems[key].hidden);
if (label.datasetIndex) {
myChart.legend.legendItems[key].hidden = true;
}
console.log(myChart.legend.legendItems[key].hidden);
});
myChart.update();
I'm trying to draw the chart so that when it first loads, every label is crossed out and hidden. DO I have the right idea setting it to hidden and updating? Thanks!
This works for me:
multiLineChart.legend.legendItems[0].hidden = true;
multiLineChart.data.datasets[0].hidden = true;
For some reason the update method didn't quite work, but this did:
function init() {
myBarChart = new Chart(ctx, {
type: chartType,
data: data,
options: options
});
myBarChart.legend.legendItems[0].hidden = true;
myBarChart.data.datasets[0].hidden = true;
}
And your function:
multiLineChart.legend.legendItems.forEach(function(label, key) {
multiLineChart.legend.legendItems[key].hidden = true;
multiLineChart.data.datasets[key].hidden = true;
});
Result:
Codepen: Chart.js Pre Crossed Series

Loading or Activating ChartJS on Load

I have Chart.js set up with a form so that it takes input fields from the form and after clicking Submit/Update button, it generates a donut chart using those numbers from the input fields as data points.
The issue is how can I get the form or Chart.js to run once and generate a donut chart with the input fields numbers when its on the page (on page load).
The form input fields are already filled with numbers/data via HTML5 Local Storage).
I've tried jQuery .submit(); on page load but nothing happens. I appreciate any help. Thank you.
EDIT: I've gone ahead and created a fiddle to re-create the issue.
$(document).ready(function () {
$("#submit-btn").click(function (e) {
e.preventDefault();
var outputyearly = $('#outputyearly');
var amount = parseInt($('#amount').val().replace(/,/g, ''), 10);
var rate = parseFloat($('#rate').val(), 10);
var term = parseFloat($('#term').val(), 10);
// Some math calculations code
if (window.onload.myDoughnutChart != null) {
window.onload.myDoughnutChart.destroy();
}
var ctx = document.getElementById("myChart").getContext("2d");
window.onload.myDoughnutChart = new Chart(ctx, {
type: 'doughnut',
data: data,
options: options
});
});
});
You can put the code that draws the chart in a function and call that function on both the $(document).ready and $("#submit-btn").click event, like this:
function drawChart() {
var outputyearly = $('#outputyearly');
var amount = parseInt($('#amount').val().replace(/,/g, ''), 10);
var rate = parseFloat($('#rate').val(), 10);
var term = parseFloat($('#term').val(), 10);
//Destroy the dougnut chart here, not sure how it's accessible, but it won't work through window.onload
var ctx = document.getElementById("myChart").getContext("2d");
new Chart(ctx, {
type: 'doughnut',
data: data,
options: options
});
}
$(document).ready(function() {
drawChart();
$("#submit-btn").click(function (e) {
e.preventDefault();
drawChart();
});
});
Note that $(document).ready already imposes the code inside of it to be executed when the page is done, so putting a window.onload inside of it won't make any difference.

How to clear a chart from a canvas so that hover events cannot be triggered?

I'm using Chartjs to display a Line Chart and this works fine:
// get line chart canvas
var targetCanvas = document.getElementById('chartCanvas').getContext('2d');
// draw line chart
var chart = new Chart(targetCanvas).Line(chartData);
But the problem occurs when I try to change the data for the Chart. I update the graph by creating a new instance of a Chart with the new data points, and thus reinitializing the canvas.
This works fine. However, when I hover over the new chart, if I happen to go over specific locations corresponding to points displayed on the old chart, the hover/label is still triggered and suddenly the old chart is visible. It remains visible while my mouse is at this location and disappears when move off that point. I don't want the old chart to display. I want to remove it completely.
I've tried to clear both the canvas and the existing chart before loading the new one. Like:
targetCanvas.clearRect(0,0, targetCanvas.canvas.width, targetCanvas.canvas.height);
and
chart.clear();
But none of these have worked so far. Any ideas about how I can stop this from happening?
I had huge problems with this
First I tried .clear() then I tried .destroy() and I tried setting my chart reference to null
What finally fixed the issue for me: deleting the <canvas> element and then reappending a new <canvas> to the parent container
My specific code (obviously there's a million ways to do this):
var resetCanvas = function(){
$('#results-graph').remove(); // this is my <canvas> element
$('#graph-container').append('<canvas id="results-graph"><canvas>');
canvas = document.querySelector('#results-graph');
ctx = canvas.getContext('2d');
ctx.canvas.width = $('#graph').width(); // resize to parent width
ctx.canvas.height = $('#graph').height(); // resize to parent height
var x = canvas.width/2;
var y = canvas.height/2;
ctx.font = '10pt Verdana';
ctx.textAlign = 'center';
ctx.fillText('This text is centered on the canvas', x, y);
};
I have faced the same problem few hours ago.
The ".clear()" method actually clears the canvas, but (evidently) it leaves the object alive and reactive.
Reading carefully the official documentation, in the "Advanced usage" section, I have noticed the method ".destroy()", described as follows:
"Use this to destroy any chart instances that are created. This will
clean up any references stored to the chart object within Chart.js,
along with any associated event listeners attached by Chart.js."
It actually does what it claims and it has worked fine for me, I suggest you to give it a try.
var myPieChart=null;
function drawChart(objChart,data){
if(myPieChart!=null){
myPieChart.destroy();
}
// Get the context of the canvas element we want to select
var ctx = objChart.getContext("2d");
myPieChart = new Chart(ctx).Pie(data, {animateScale: true});
}
This is the only thing that worked for me:
document.getElementById("chartContainer").innerHTML = ' ';
document.getElementById("chartContainer").innerHTML = '<canvas id="myCanvas"></canvas>';
var ctx = document.getElementById("myCanvas").getContext("2d");
We can update the chart data in Chart.js V2.0 as follows:
var myChart = new Chart(ctx, data);
myChart.config.data = new_data;
myChart.update();
I had the same problem here... I tried to use destroy() and clear() method, but without success.
I resolved it the next way:
HTML:
<div id="pieChartContent">
<canvas id="pieChart" width="300" height="300"></canvas>
</div>
Javascript:
var pieChartContent = document.getElementById('pieChartContent');
pieChartContent.innerHTML = ' ';
$('#pieChartContent').append('<canvas id="pieChart" width="300" height="300"><canvas>');
ctx = $("#pieChart").get(0).getContext("2d");
var myPieChart = new Chart(ctx).Pie(data, options);
It works perfect to me... I hope that It helps.
This worked very well for me
var ctx = $("#mycanvas");
var LineGraph = new Chart(ctx, {
type: 'line',
data: chartdata});
LineGraph.destroy();
Use .destroy this to destroy any chart instances that are created. This will clean up any references stored to the chart object within Chart.js, along with any associated event listeners attached by Chart.js. This must be called before the canvas is reused for a new chart.
It's best to use Chart.js specific functionalities to initially check for the existing chart instance and then perform destroy or clear in order to reuse the same canvas element for rendering another chart, instead of handlding HTML elements from within JS.
ChartJs's getChart(key) - finds the chart instance from the given key.
If the key is a string, it is interpreted as the ID of the Canvas element for the Chart.
The key can also be a CanvasRenderingContext2D or an HTMLDOMElement.
Note: This will return undefined if no Chart is found. If the instance of the chart is found, it signifies that the chart must have previously been created.
// JS - Destroy exiting Chart Instance to reuse <canvas> element
let chartStatus = Chart.getChart("myChart"); // <canvas> id
if (chartStatus != undefined) {
chartStatus.destroy();
//(or)
// chartStatus.clear();
}
//-- End of chart destroy
var chartCanvas = $('#myChart'); //<canvas> id
chartInstance = new Chart(chartCanvas, {
type: 'line',
data: data
});
<!-- HTML -Line Graph - Chart.js -->
<div class="container-fluid" id="chartContainer">
<canvas id="myChart" width="400" height="150"> </canvas>
</div>
This approach would save you from remove - create - append a Canvas element into DIV from inside JS.
Simple edit for 2020:
This worked for me. Change the chart to global by making it window owned (Change the declaration from var myChart to window myChart)
Check whether the chart variable is already initialized as Chart, if so, destroy it and create a new one, even you can create another one on the same name. Below is the code:
if(window.myChart instanceof Chart)
{
window.myChart.destroy();
}
var ctx = document.getElementById('myChart').getContext("2d");
Hope it works!
Complementing Adam's Answer
With Vanilla JS:
document.getElementById("results-graph").remove(); //canvas
div = document.querySelector("#graph-container"); //canvas parent element
div.insertAdjacentHTML("afterbegin", "<canvas id='results-graph'></canvas>"); //adding the canvas again
Using CanvasJS, this works for me clearing chart and everything else, might work for you as well, granting you set your canvas/chart up fully before each processing elsewhere:
var myDiv= document.getElementById("my_chart_container{0}";
myDiv.innerHTML = "";
I couldn't get .destroy() to work either so this is what I'm doing. The chart_parent div is where I want the canvas to show up. I need the canvas to resize each time, so this answer is an extension of the above one.
HTML:
<div class="main_section" >
<div id="chart_parent"></div>
<div id="legend"></div>
</div>
JQuery:
$('#chart').remove(); // this is my <canvas> element
$('#chart_parent').append('<label for = "chart">Total<br /><canvas class="chart" id="chart" width='+$('#chart_parent').width()+'><canvas></label>');
When you create one new chart.js canvas, this generate one new iframe hidden, you need delete the canvas and the olds iframes.
$('#canvasChart').remove();
$('iframe.chartjs-hidden-iframe').remove();
$('#graph-container').append('<canvas id="canvasChart"><canvas>');
var ctx = document.getElementById("canvasChart");
var myChart = new Chart(ctx, { blablabla });
reference:
https://github.com/zebus3d/javascript/blob/master/chartJS_filtering_with_checkboxs.html
This worked for me.
Add a call to clearChart, at the top oF your updateChart()
`function clearChart() {
event.preventDefault();
var parent = document.getElementById('parent-canvas');
var child = document.getElementById('myChart');
parent.removeChild(child);
parent.innerHTML ='<canvas id="myChart" width="350" height="99" ></canvas>';
return;
}`
Since destroy kind of destroys "everything", a cheap and simple solution when all you really want is to just "reset the data". Resetting your datasets to an empty array will work perfectly fine as well. So, if you have a dataset with labels, and an axis on each side:
window.myLine2.data.labels = [];
window.myLine2.data.datasets[0].data = [];
window.myLine2.data.datasets[1].data = [];
After this, you can simply call:
window.myLine2.data.labels.push(x);
window.myLine2.data.datasets[0].data.push(y);
or, depending whether you're using a 2d dataset:
window.myLine2.data.datasets[0].data.push({ x: x, y: y});
It'll be a lot more lightweight than completely destroying your whole chart/dataset, and rebuilding everything.
If you are using chart.js in an Angular project with Typescript, the you can try the following;
Import the library:
import { Chart } from 'chart.js';
In your Component Class declare the variable and define a method:
chart: Chart;
drawGraph(): void {
if (this.chart) {
this.chart.destroy();
}
this.chart = new Chart('myChart', {
.........
});
}
In HTML Template:
<canvas id="myChart"></canvas>
What we did is, before initialization of new chart, remove/destroy the previews Chart instance, if exist already, then create a new chart, for example
if(myGraf != undefined)
myGraf.destroy();
myGraf= new Chart(document.getElementById("CanvasID"),
{
...
}
Hope this helps.
First put chart in some variable then history it next time before init
#Check if myChart object exist then distort it
if($scope.myChart) {
$scope.myChart.destroy();
}
$scope.myChart = new Chart(targetCanvas
You should save the chart as a variable.
On global scope, if its pure javascript, or as a class property, if its Angular.
Then you'll be able to use this reference to call destroy().
Pure Javascript:
var chart;
function startChart() {
// Code for chart initialization
chart = new Chart(...); // Replace ... with your chart parameters
}
function destroyChart() {
chart.destroy();
}
Angular:
export class MyComponent {
chart;
constructor() {
// Your constructor code goes here
}
ngOnInit() {
// Probably you'll start your chart here
// Code for chart initialization
this.chart = new Chart(...); // Replace ... with your chart parameters
}
destroyChart() {
this.chart.destroy();
}
}
For me this worked:
var in_canvas = document.getElementById('chart_holder');
//remove canvas if present
while (in_canvas.hasChildNodes()) {
in_canvas.removeChild(in_canvas.lastChild);
}
//insert canvas
var newDiv = document.createElement('canvas');
in_canvas.appendChild(newDiv);
newDiv.id = "myChart";
Chart.js has a bug:
Chart.controller(instance) registers any new chart in a global property Chart.instances[] and deletes it from this property on .destroy().
But at chart creation Chart.js also writes ._meta property to dataset variable:
var meta = dataset._meta[me.id];
if (!meta) {
meta = dataset._meta[me.id] = {
type: null,
data: [],
dataset: null,
controller: null,
hidden: null, // See isDatasetVisible() comment
xAxisID: null,
yAxisID: null
};
and it doesn't delete this property on destroy().
If you use your old dataset object without removing ._meta property, Chart.js will add new dataset to ._meta without deletion previous data. Thus, at each chart's re-initialization your dataset object accumulates all previous data.
In order to avoid this, destroy dataset object after calling Chart.destroy().
for those who like me use a function to create several graphics and want to update them a block too, only the function .destroy() worked for me, I would have liked to make an .update(), which seems cleaner but ... here is a code snippet that may help.
var SNS_Chart = {};
// IF LABELS IS EMPTY (after update my datas)
if( labels.length != 0 ){
if( Object.entries(SNS_Chart).length != 0 ){
array_items_datas.forEach(function(for_item, k_arr){
SNS_Chart[''+for_item+''].destroy();
});
}
// LOOP OVER ARRAY_ITEMS
array_items_datas.forEach(function(for_item, k_arr){
// chart
OPTIONS.title.text = array_str[k_arr];
var elem = document.getElementById(for_item);
SNS_Chart[''+for_item+''] = new Chart(elem, {
type: 'doughnut',
data: {
labels: labels[''+for_item+''],
datasets: [{
// label: '',
backgroundColor: [
'#5b9aa0',
'#c6bcb6',
'#eeac99',
'#a79e84',
'#dbceb0',
'#8ca3a3',
'#82b74b',
'#454140',
'#c1502e',
'#bd5734'
],
borderColor: '#757575',
borderWidth : 2,
// hoverBackgroundColor : '#616161',
data: datas[''+for_item+''],
}]
},
options: OPTIONS
});
// chart
});
// END LOOP ARRAY_ITEMS
}
// END IF LABELS IS EMPTY ...
just declare let doughnut = null before creating your chart
const doughnutDriverStatsChartCanvas = $('#dougnautChartDriverStats').get(0).getContext('2d')
const doughnutOptionsDriverStats = {
maintainAspectRatio: false,
responsive: true,
}
let doughnut = null
doughnut = new Chart(doughnutDriverStatsChartCanvas, {
type: 'doughnut',
data: doughnutChartDriverStats,
options: doughnutOptionsDriverStats
})

Categories