Chartjs Bar Chart showing old data when hovering

JavascriptJquerychart.js

Javascript Problem Overview


I have a bar chart that's created using chart.js. Everything works fine on page load, but when I change the time frame using a daterangepicker, a glitch appears. The new data is brought in, but when I hover over it, the old data is shown. I'm new to javascript so I'm hoping to get some help. It looks like I need to incorporate .destroy(); somehow, but I don't know how. A snippet of my code is below:

function loadFTPRChart(startdate, enddate){
var BCData = {
labels: [],
datasets: [
 {
  label: "Pass %",
  backgroundColor: "#536A7F",
  data: [],
  stack: 1
},
{
  label: "Fail %",
  backgroundColor: "#e6e6e6",
  data: [],
  stack: 1
},
{
  label: "Auto %",
  backgroundColor: "#286090",
  data: [],
  stack: 2
},
{
  label: "Manual %",
  backgroundColor: "#f0f0f0",
  data: [],
  stack: 2
}
 ]
};
  $.getJSON( "content/FTPR_AM_Graph_ajax.php", {
    startdate: startdate,
    enddate: enddate,
    location: "M"
})
.done(function( data ) {
    console.log("data", data);
    $.each( data.aaData, function( key, val ) {
      if(val == ""){return true}
      BCData.labels.push("Coater " + val[0]);
      BCData.datasets[0].data.push(parseFloat(val[2]));
      BCData.datasets[1].data.push(parseFloat(100-val[2]));
      BCData.datasets[2].data.push(parseFloat(val[1]));
      BCData.datasets[3].data.push(parseFloat(100-val[1]));
    });

    var option = {   
     responsive:true,
};
console.log("BCData", BCData);


//console.log("PrevData", data);
var ctx = document.getElementById("mybarChart2").getContext("2d");
new Chart(ctx, {
  type: 'groupableBar',
  data: BCData,
  options: {
    scales: {
      yAxes: [{
        ticks: {
          max: 100,
        },
        stacked: true,
      }]
    }
  }
});
});

}


loadFTPRChart($('#reportrange').data().daterangepicker.startDate.format('MM/DD/YYYY'), $('#reportrange').data().daterangepicker.endDate.format('MM/DD/YYYY'));

What is the best way to destroy the original data so that when I change the date range and hover over the new chart, the old data no longer flickers?

Thanks

Javascript Solutions


Solution 1 - Javascript

With the approach you are taking (e.g. creating a new Chart object each time the date range changes), then you must first destroy the previous chart and then create the new one.

You can use the .destroy() prototype method to do this. Here is exactly what the API states.

> 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. This > must be called before the canvas is reused for a new chart.

Therefore, your code would look something like this (notice that we destroy and re-create).

// define a variable to store the chart instance (this must be outside of your function)
var myChart;

function loadFTPRChart(startdate, enddate) {
  var BCData = {
    labels: [],
    datasets: [{
      label: "Pass %",
      backgroundColor: "#536A7F",
      data: [],
      stack: 1
    }, {
      label: "Fail %",
      backgroundColor: "#e6e6e6",
      data: [],
      stack: 1
    }, {
      label: "Auto %",
      backgroundColor: "#286090",
      data: [],
      stack: 2
    }, {
      label: "Manual %",
      backgroundColor: "#f0f0f0",
      data: [],
      stack: 2
    }]
  };
  
  $.getJSON("content/FTPR_AM_Graph_ajax.php", {
      startdate: startdate,
      enddate: enddate,
      location: "M"
    })
    .done(function(data) {
      console.log("data", data);
      $.each(data.aaData, function(key, val) {
        if (val == "") {
          return true
        }
        BCData.labels.push("Coater " + val[0]);
        BCData.datasets[0].data.push(parseFloat(val[2]));
        BCData.datasets[1].data.push(parseFloat(100 - val[2]));
        BCData.datasets[2].data.push(parseFloat(val[1]));
        BCData.datasets[3].data.push(parseFloat(100 - val[1]));
      });

      var option = {
        responsive: true,
      };
      console.log("BCData", BCData);
      
      // if the chart is not undefined (e.g. it has been created)
      // then destory the old one so we can create a new one later
      if (myChart) {
      	myChart.destroy();
      }

      var ctx = document.getElementById("mybarChart2").getContext("2d");
      myChart = new Chart(ctx, {
        type: 'groupableBar',
        data: BCData,
        options: {
          scales: {
            yAxes: [{
              ticks: {
                max: 100,
              },
              stacked: true,
            }]
          }
        }
      });
    });
}

With that said, it's expensive the destroy/create over and over and actually it isn't even necessary. There is another prototype method called .update() that you can use to just re-render the chart if you have changed it's underlying data or label objects.

Here is a jsfiddle showing an example of changing the underlying data and labels and then re-rendering the chart. I would highly recommend you take this approach instead.

Here is how your code would look taking this better approach.

// define a variable to store the chart instance (this must be outside of your function)
var myChart;

function loadFTPRChart(startdate, enddate) {
  var BCData = {
    labels: [],
    datasets: [{
      label: "Pass %",
      backgroundColor: "#536A7F",
      data: [],
      stack: 1
    }, {
      label: "Fail %",
      backgroundColor: "#e6e6e6",
      data: [],
      stack: 1
    }, {
      label: "Auto %",
      backgroundColor: "#286090",
      data: [],
      stack: 2
    }, {
      label: "Manual %",
      backgroundColor: "#f0f0f0",
      data: [],
      stack: 2
    }]
  };
  
  $.getJSON("content/FTPR_AM_Graph_ajax.php", {
      startdate: startdate,
      enddate: enddate,
      location: "M"
    })
    .done(function(data) {
      console.log("data", data);
      $.each(data.aaData, function(key, val) {
        if (val == "") {
          return true
        }
        BCData.labels.push("Coater " + val[0]);
        BCData.datasets[0].data.push(parseFloat(val[2]));
        BCData.datasets[1].data.push(parseFloat(100 - val[2]));
        BCData.datasets[2].data.push(parseFloat(val[1]));
        BCData.datasets[3].data.push(parseFloat(100 - val[1]));
      });

      var option = {
        responsive: true,
      };
      console.log("BCData", BCData);
      
      // if the chart is not undefined (e.g. it has been created)
      // then just update the underlying labels and data for each
      // dataset and re-render the chart
      if (myChart) {
      	myChart.data.labels = BCData.labels;
        myChart.data.datasets[0].data = BCData.datasets[0].data;
        myChart.data.datasets[1].data = BCData.datasets[1].data;
        myChart.data.datasets[2].data = BCData.datasets[2].data;
        myChart.data.datasets[3].data = BCData.datasets[3].data;
        myChart.update();
      } else {
      	// otherwise, this is the first time we are loading so create the chart
        var ctx = document.getElementById("mybarChart2").getContext("2d");
        myChart = new Chart(ctx, {
          type: 'groupableBar',
          data: BCData,
          options: {
            scales: {
              yAxes: [{
                ticks: {
                  max: 100,
                },
                stacked: true,
              }]
            }
          }
        });
      }
    });
}

Solution 2 - Javascript

This is chartjs trick i found

var ctxLine = document.getElementById("line-chart").getContext("2d");
if(window.bar != undefined) 
window.bar.destroy(); 
window.bar = new Chart(ctxLine, {});

https://therichpost.com/solved-hovering-chartjs-bar-chart-showing-old-data

Solution 3 - Javascript

It will help you...

Check whether MyChart have already configurations or not, If it exist clear it with destroy(); method, And bind new configuration to canvas.

Sample Code..

if (window.MyChart != undefined)
{
    window.MyChart.destroy();
}
window.MyChart = new Chart(ctx, MyChartconfig);

Solution 4 - Javascript

There is a simple solution for this, which can be done in JS itself.

let's say your html has something like below

<div id="chartContainer">
 <canvas id="mybarChart2"></canvas>
</div>

Then in your script, you can add below lines of code before updating the data.

document.getElementById("chartContainer").innerHTML = '&nbsp;';
document.getElementById("chartContainer").innerHTML = '<canvas id="mybarChart2"></canvas>';
var ctx = document.getElementById("mybarChart2").getContext("2d");

This solved my issue.

Solution 5 - Javascript

after stackoverflowing for so many hours i found and easy solution to it no need to do so many changes ... just add this line in options section

events:[]

in the options section it is an quick solution to get rid from Chartjs Bar Chart showing old data when hovering

if You need Hovering event also then try to reinitialise or re-render the canvas adding if conditions this will fix for sure

Solution 6 - Javascript

I was found the problem and my solution is remove them from html first and before load new chart then append canvas.

Html

 $('#chart').empty();
    
$('#chart').html('<canvas id="survey_result"  width="400" height="200"></canvas>'); // then load chart.
    
var ctx = document.getElementById("survey_result");

    <div id="chart" class="display>
    </div>

Solution 7 - Javascript

Declare myChart as a global variable

let mychart;

Before graph creation just write

function pieChart(pdata) {
        // destroy previous created graph
        if (myChart) {
            myChart.destroy()
        }
        let ctx = document.getElementById("pie-chart").getContext('2d');
         myChart = new Chart(ctx, {

            type: 'doughnut',
            data: {
                labels: ['Count'],
                datasets: [{
                    backgroundColor: [
                        "#2ecc71",
                    ],
                    data: [pdata],

                }]
            },
            
        });
    }

why we adding this? Because, if we create any graph at first time the mychart create a object, in that case when we rander it, it won't be change. that's why we need to destroy previous object and create new object for newer visualization.

Solution 8 - Javascript

The code of the provided samples by chartjs.org have shown that they don't destroy() the chart and create a new one. Instead, they pop() the existing data from the chart and push() the new dataset to the graph and then update() the chart.

This code is from the chartjs.org website which removes the dataset from the chart by POP().

	document.getElementById('removeDataset').addEventListener('click', function() {
		horizontalBarChartData.datasets.pop();
		window.myHorizontalBar.update();
	});

And this code is for adding the dataset to the chart by PUSH():

	document.getElementById('addDataset').addEventListener('click', function() {
		var colorName = colorNames[horizontalBarChartData.datasets.length % colorNames.length];
		var dsColor = window.chartColors[colorName];
		var newDataset = {
			label: 'Dataset ' + (horizontalBarChartData.datasets.length + 1),
			backgroundColor: color(dsColor).alpha(0.5).rgbString(),
			borderColor: dsColor,
			data: []
		};

		for (var index = 0; index < horizontalBarChartData.labels.length; ++index) {
			newDataset.data.push(randomScalingFactor());
		}

		horizontalBarChartData.datasets.push(newDataset);
		window.myHorizontalBar.update();
	});

The last step of these two code blocks is to update the chart.

Generally speaking, it is necessary to pop the data that you want to remove from the chart and then push the new data and finally update the chart. Therefore, the pervious data is not going to be shown when hovering.

Solution 9 - Javascript

I know this is old, but most of these didn't work in my situation, so I'm posting what worked for me.

I had two charts exhibiting this behavior, a z-stacked bar chart and a pie chart. I tried these to no avail:

  • myChart.destroy(): This would work to change the values, but for some reason also affected the size and display values for my charts
  • options: { events: [] }: As said in that post, this removes all tooltips on hover, which I still wanted
  • innerHTML: I don't know if this is a newer feature of chartjs, but none of my charts ever had an innerHTML attribute apart from ""
  • myChart.datasets.pop(): This one is the spirit behind my solution, but in most cases I had more than one dataset, so I just removed them all:

  if (myChart !== undefined) {
    while (myChart.data.datasets.length > 0) {
      myChart.data.datasets.pop();
    }
  }

I was also previously creating var myChart with the function I was using to create the chart and organize the data. After moving that variable declaration outside the function and implementing the above within the function, they work like a charm!

Solution 10 - Javascript

What worked for me (maybe it's ineffective but this isn't a bit issue to me as it would only be done a few times per visit) - destroying and recreating the whole canvas object (using JQuery).

$("#canvasID").remove();
$("<canvas>").attr({
	id: "canvasID"
}).appendTo("#canvasParent");

Solution 11 - Javascript

I could not get the "destroy()" to work at all. I struggled. I ended up removing the element altogether, then recreating on each update of data.

        if(document.getElementById(chartID)) {
			document.getElementById(chartID).remove();
		}
		

		var canvasParent;
        // I had 2 charts, so a ternary to select the right element
		chartID == 'fleet-fuel-chartComp1' ? canvasParent = document.getElementById('canvas-node1') : canvasParent = document.getElementById('canvas-node2');
		
		var canvas = document.createElement('canvas');
		canvas.id = chartID;
		canvasParent.appendChild(canvas);

		var ffc = document.getElementById(chartID);

Solution 12 - Javascript

Why this Problem Happened

This problem occurs, when we change the graph data by ajax. it will append the data in the graph. when we hover on that previous point it highligts the previous graph.

Soluctions: It will work 100%.

$('#chart').html('');
        $('#chart').html('<canvas id="Chartcanvas" style="height: 350px;"></canvas>');

Solution 13 - Javascript

I tried all the suggestions above and they did not work, it turned out my function:

function initChart(data, label) {
	if(!data){
		data = sanitizeData({!! json_encode($subsequentSickdays) !!});
		label = "Employees with X amount of sickdays";
	}
	
	$('#chartjs-custom').html('');
	$('#chartjs-custom').html('<canvas id="chart-absence" class="js-chart"></canvas>');
	
	return $.HSCore.components.HSChartJS.init($('#chart-absence'), {
		"type": "pie",
		"data":	setChartData(data, label),
		"options": options()
	});
	
}

was being initialised twice on page load. One when i defined var chart = initChart() and when i initialised it with initChart(). After i removed initChart() it worked. Don't know why this would matter but hey, it works, hope this helps someone.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDjones12View Question on Stackoverflow
Solution 1 - JavascriptjordanwillisView Answer on Stackoverflow
Solution 2 - JavascriptRodener DajesView Answer on Stackoverflow
Solution 3 - JavascriptDinesh ChileView Answer on Stackoverflow
Solution 4 - JavascriptRajeshwarView Answer on Stackoverflow
Solution 5 - JavascriptRaja Shekhar ReddyView Answer on Stackoverflow
Solution 6 - JavascriptChet Tudtude AiemView Answer on Stackoverflow
Solution 7 - JavascriptNur Amin SifatView Answer on Stackoverflow
Solution 8 - JavascriptRezaView Answer on Stackoverflow
Solution 9 - JavascriptjlehenbauerView Answer on Stackoverflow
Solution 10 - JavascriptDrFlarreView Answer on Stackoverflow
Solution 11 - JavascriptPaul PickinsView Answer on Stackoverflow
Solution 12 - JavascriptKaleemullahView Answer on Stackoverflow
Solution 13 - JavascriptJepardView Answer on Stackoverflow