How to access all data in a function with ChartJs? - javascript

Consider the following code.
var chartData = {
labels: ["January", "February", "March", "April", "May", "June"],
datasets: [{
fillColor: "#79D1CF",
strokeColor: "#79D1CF",
data: [60, 80, 81, 56, 55, 40]
}]
};
var ctx = document.getElementById("myChart1").getContext("2d");
var myLine = new Chart(ctx, {
type: "line",
data: chartData,
showTooltips: false,
onAnimationComplete: function() {
var ctx = this.chart.ctx;
ctx.font = this.scale.font;
ctx.fillStyle = this.scale.textColor
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
this.datasets.forEach(function(dataset) {
dataset.points.forEach(function(points) {
ctx.fillText(points.value, points.x, points.y - 10);
});
})
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.2/chart.min.js"></script>
<canvas id="myChart1" height="300" width="500"></canvas>
Because I use the function in the onAnimationComplete in different graphs, I like to create a JavaScript file where I collect all the functions and use them. For example
var chart = new Chart(ctx, eval(config));
chart.options.animation.onComplete = function () {
window["AnimationComplete1"](chart);
}
function AnimationComplete1(chart) {
var ctx = chart.ctx;
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
chart.data.datasets.forEach(function (dataset) {
dataset.points.forEach(function (points) {
ctx.fillText(points.value, points.x, points.y - 10);
});
})
}
In this code, the problem is that in the function I don't have access to the point from the dataset from chart.data.datasets. Also, I don't have access to the scale for font and textColor.
Is there a way to access those values from a common function?

The Chart.js animation onComplete callback function must be defined inside the chart options.
options: {
animation: {
onComplete: ctx => {
// do your stuff
}
}
}
Please take a look at your amended code below and see how it works.
var chartData = {
labels: ["January", "February", "March", "April", "May", "June"],
datasets: [{
fillColor: "#79D1CF",
strokeColor: "#79D1CF",
data: [60, 80, 81, 56, 55, 40]
}]
};
new Chart('myChart1', {
type: "line",
data: chartData,
options: {
animation: {
onComplete: ctx => {
console.log(ctx.chart.data.datasets);
console.log(ctx.chart.options.scales.x);
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.8.0/chart.min.js"></script>
<canvas id="myChart1" height="300" width="500"></canvas>

Related

Moving vertical line when hovering over the chart using chart.js in v2.9.4

I'm using a code that was used to work before. Now it is not working in the new version v2.9.4. I'm giving the old code below:
Chart.defaults.LineWithLine = Chart.defaults.line;
Chart.controllers.LineWithLine = Chart.controllers.line.extend({
draw: function(ease) {
Chart.controllers.line.prototype.draw.call(this, ease);
if (this.chart.tooltip._active && this.chart.tooltip._active.length) {
var activePoint = this.chart.tooltip._active[0],
ctx = this.chart.ctx,
x = activePoint.tooltipPosition().x,
topY = this.chart.legend.bottom,
bottomY = this.chart.chartArea.bottom;
// draw line
ctx.save();
ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.lineWidth = 2;
ctx.strokeStyle = '#07C';
ctx.stroke();
ctx.restore();
}
}
});
You could add the following to the chart options:
options: {
tooltips: {
mode: 'x',
intersect: false
},
...
Please consult Tooltip Configuration and Interaction Modes from Chart.js documentation.
Chart.defaults.LineWithLine = Chart.defaults.line;
Chart.controllers.LineWithLine = Chart.controllers.line.extend({
draw: function(ease) {
Chart.controllers.line.prototype.draw.call(this, ease);
if (this.chart.tooltip._active && this.chart.tooltip._active.length) {
var activePoint = this.chart.tooltip._active[0],
ctx = this.chart.ctx,
x = activePoint.tooltipPosition().x,
topY = this.chart.legend.bottom,
bottomY = this.chart.chartArea.bottom;
ctx.save();
ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.lineWidth = 2;
ctx.strokeStyle = '#07C';
ctx.stroke();
ctx.restore();
}
}
});
new Chart("chart", {
type: 'LineWithLine',
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My Dataset",
data: [65, 59, 80, 81, 56, 55, 40]
}]
},
options: {
tooltips: {
mode: 'x',
intersect: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="chart" height="90"></canvas>
This question is two years old. Nowadays, we can achieve this using plugins and hook calls in this case beforeTooltipDraw to capture the tooltip.caretX. Also we can use the build-in interaction option to achieve this.
const $chart = document.getElementById('chart')
const plugin = {
id: 'verticalLiner',
afterInit: (chart, args, opts) => {
chart.verticalLiner = {}
},
afterEvent: (chart, args, options) => {
const {inChartArea} = args
chart.verticalLiner = {draw: inChartArea}
},
beforeTooltipDraw: (chart, args, options) => {
const {draw} = chart.verticalLiner
if (!draw) return
const {ctx} = chart
const {top, bottom} = chart.chartArea
const {tooltip} = args
const x = tooltip?.caretX
if (!x) return
ctx.save()
ctx.beginPath()
ctx.moveTo(x, top)
ctx.lineTo(x, bottom)
ctx.stroke()
ctx.restore()
}
}
const data = {
labels: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"],
datasets: [{
data: [12, 3, 2, 1, 8, 8, 2, 2, 3, 5, 7, 1]
}]
}
const options = {
type: 'line',
data,
options: {
maintainAspectRatio: false,
interaction: {
mode: 'index',
intersect: false,
},
plugins: {
verticalLiner: {}
}
},
plugins: [plugin]
}
const chart = new Chart($chart, options)
<div class="wrapper" style="width: 98vw; height:180px">
<canvas id="chart"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>

chart.js Is there way to color line chart ticks depending on value

I have simple chart.js line chart:
I need to color lines between ticks, depending on previous value like that:
If next value is lower than prev line must be red, if higher color is green.
Any ideas how to achieve this?
You can check the following solution.
var ctx = document.getElementById('myChart').getContext('2d');
//adding custom chart type
Chart.defaults.multicolorLine = Chart.defaults.line;
Chart.controllers.multicolorLine = Chart.controllers.line.extend({
draw: function(ease) {
var
startIndex = 0,
meta = this.getMeta(),
points = meta.data || [],
colors = this.getDataset().colors,
area = this.chart.chartArea,
originalDatasets = meta.dataset._children
.filter(function(data) {
return !isNaN(data._view.y);
});
function _setColor(newColor, meta) {
meta.dataset._view.borderColor = newColor;
}
if (!colors) {
Chart.controllers.line.prototype.draw.call(this, ease);
return;
}
for (var i = 2; i <= colors.length; i++) {
if (colors[i-1] !== colors[i]) {
_setColor(colors[i-1], meta);
meta.dataset._children = originalDatasets.slice(startIndex, i);
meta.dataset.draw();
startIndex = i - 1;
}
}
meta.dataset._children = originalDatasets.slice(startIndex);
meta.dataset.draw();
meta.dataset._children = originalDatasets;
points.forEach(function(point) {
point.draw(area);
});
}
});
// build colors sequence
const data = [0, 10, 5, 2, 20, 30, 45];
const availableColors = ['red', 'green'];
let colors = [];
data.forEach(item => {
availableColors.forEach(color => {
colors.push(color)
})
})
var chart = new Chart(ctx, {
// The type of chart we want to create
type: 'multicolorLine',
// The data for our dataset
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
borderColor: 'rgb(255, 99, 132)',
data: data,
//first color is not important
colors: ['', ...colors]
}]
},
// Configuration options go here
options: {}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.bundle.min.js"></script>
<canvas id="myChart"></canvas>

.datasets is undefined when trying to access bars from chart.js

I'm trying to recreate this example:
chart.js bar chart color change based on value
With the following code
<script src="/chart.js/dist/Chart.js"></script>
<canvas id="myChart" width="400" height="400"></canvas>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
window.myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 3, 3],
fillColor: "rgba(220,220,220,0.5)",
strokeColor: "rgba(220,220,220,0.8)",
highlightFill: "rgba(220,220,220,0.75)",
highlightStroke: "rgba(220,220,220,1)",
borderWidth: 1
}]
},
});
var bars = myChart.datasets[0].bars;
for (i = 0; i < bars.length; i++) {
var color = "green";
//You can check for bars[i].value and put your conditions here
if (bars[i].value < 3) {
color = "red";
} else if (bars[i].value < 5) {
color = "orange"
} else if (bars[i].value < 8) {
color = "yellow"
} else {
color = "green"
}
bars[i].fillColor = color;
}
myChart.update();
</script>
but I get in console the TypeError:
myChart.datasets is undefined on the line var bars = myChart.datasets[0].bars;
Do you have an idea what I'm overlooking?
Thank you
Here is the complete example you want.
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>
<canvas id="myChart" width="400" height="400"></canvas>
<script>
//Creating a barchart with default values
var myChart = new Chart(document.getElementById("myChart"), {
"type": "bar",
"data": {
"labels": ["January", "February", "March", "April", "May", "June", "July"],
"datasets": [{
"label": "My First Dataset",
"data": [65, 59, 80, 81, 56, 55, 40],
"fill": false,
"backgroundColor": ["#fb4d4d", "#fb9d4d", "#f8fb4d", "#98fb4d", "#4effee", "#4cb9f8", "#574cf8"],
"borderColor": ["#fb4d4d", "#fb9d4d", "#f8fb4d", "#98fb4d", "#4effee", "#4cb9f8", "#574cf8"],
"borderWidth": 1
}]
},
"options": {
"scales": {
"yAxes": [{
"ticks": {
"beginAtZero": true
}
}]
}
}
});
//Getting the bar-chart existing values
var bars = myChart.config.data.datasets[0];
var data = bars.data;
//Updating the existing value (object which holds value)
for (i = 0; i < data.length; i++) {
var bgcolor = "";
var brcolor = "";
if (data[i] < 30) {
bgcolor = "red";
brcolor = "red";
} else if (data[i] < 50) {
bgcolor = "orange";
brcolor = "orange";
} else if (data[i] < 80) {
bgcolor = "yellow";
brcolor = "yellow";
} else {
bgcolor = "green";
brcolor = "green";
}
bars.backgroundColor[i] = bgcolor;
bars.borderColor[i] = brcolor;
}
//Triggering the chart update in 3 seconds.
setTimeout(function(){
myChart.update();
}, 3000);
</script>
your dataset was empty, was not being done as the example quoted
The correct way to do as the example you mentioned is:
var barChartData = {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [
{
label: '# of Votes',
data: [12, 19, 3, 5, 3, 3],
fillColor: "rgba(220,220,220,0.5)",
strokeColor: "rgba(220,220,220,0.8)",
highlightFill: "rgba(220,220,220,0.75)",
highlightStroke: "rgba(220,220,220,1)",
borderWidth: 1
}
]
};
var ctx = document.getElementById('myChart').getContext('2d');
window.myObjBar = new Chart(ctx).Bar(barChartData, {
responsive : true
});
var bars = myObjBar.datasets[0].bars;
for(i=0;i<bars.length;i++){
var color="green";
//You can check for bars[i].value and put your conditions here
if(bars[i].value<3){
color="red";
}
else if(bars[i].value<5){
color="orange"
}
else if(bars[i].value<8){
color="yellow"
}
else{
color="green"
}
bars[i].fillColor = color;
}
myObjBar.update(); //update the cahrt
Here is an example working :)

Label is not showing in ChartJS

I'm using ChartJS for my implementation of charts, but I notice that one of my graph's label is hidden. It is not showing its label above the bar. I've added a screenshot below for the comparison of two different bar graphs. The left graph shows the label even if it is on the very top but the other one is not showing. Please see my screenshot and code below.
function createChart(context, type, bgColor, bdColor, labels, actualData, options = {}){
new Chart(context, {
type: type,
data: {
labels: labels,
datasets: [{
label: "Actual",
backgroundColor: bgColor,
borderColor: bdColor,
data: actualData,
}]
},
options: options
});
}
function getOptions(displayLegend = true){
return {
events: false,
showTooltips: false,
legend: {
display: displayLegend
},
animation: {
duration: 0,
onComplete: function(){
var ctx = this.chart.ctx;
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseLine = 'bottom';
ctx.fillStyle = '#0b7707';
this.data.datasets.forEach(function(dataset){
console.log(dataset);
for(var i = 0; i < dataset.data.length; i++){
for(var key in dataset._meta){
var model = dataset._meta[key].data[i]._model;
ctx.fillText(dataset.data[i], model.x, model.y - 13);
}
}
});
}
}
};
}
I solved this problem by adding an empty title to the chart, so it will create space above the chart and show labels above the bar
options: {
title: {
display: true,
text: ' '
},
....
This looks like a clear case of the datalabels going out of the canvas since the bar takes height dynamically as per the data values. You can set the max y-tick setting to solve this. Here is the jsfiddle -> https://jsfiddle.net/Luaf2tm4/5979/
Hope it helps!
var canvas = document.getElementById('myChart');
var data = {
labels: ["January", "February", "March", "April", "May", "June"],
datasets: [{
label: "My First dataset",
backgroundColor: "rgba(255,99,132,0.2)",
borderColor: "rgba(255,99,132,1)",
borderWidth: 2,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
data: [500, 2000, 800, 600, 950, 890],
}]
};
function getOptions(displayLegend = false) {
return {
events: false,
showTooltips: false,
legend: {
display: displayLegend
},
scales: {
yAxes: [{
display: true,
stacked: true,
ticks: {
stepSize: 200,
min: 0, // minimum value
max: 2200 // maximum value, you can either hard code if you know your datainput, else computer the value through some logic i.e taking the max value from the dataset and adding some extra value to it.
}
}]
},
animation: {
duration: 0,
onComplete: function() {
var ctx = this.chart.ctx;
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseLine = 'bottom';
ctx.fillStyle = '#0b7707';
this.data.datasets.forEach(function(dataset) {
console.log(dataset);
for (var i = 0; i < dataset.data.length; i++) {
for (var key in dataset._meta) {
var model = dataset._meta[key].data[i]._model;
ctx.fillText(dataset.data[i], model.x, model.y - 10);
}
}
});
}
}
};
}
var myBarChart = Chart.Bar(canvas, {
data: data,
options: getOptions()
});

Chart.js update function (chart,labels,data) will not update the chart

I cannot debug the following code. I would like to update chart data (not add on top; delete current data and add completely new dataset). (Not)Working example on codepen:
https://codepen.io/anon/pen/bvBxpr
var config = {
type: 'line',
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
data: [65, 0, 80, 81, 56, 85, 40],
fill: false
}]
}
};
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, config);
labelsNew = ["Why", "u", "no", "work", "???"];
dataNew = [2, 4, 5, 6, 10];
function updateData(chart, label, data) {
removeData();
chart.data.labels.push(label);
chart.data.datasets.forEach((dataset) => {
dataset.data.push(data);
});
chart.update();
};
function removeData(chart) {
chart.data.labels.pop();
chart.data.datasets.forEach((dataset) => {
dataset.data.pop();
});
chart.update();
}
$('.button-container').on('click', 'button', updateData(myChart, labelsNew, dataNew));
I figured it out. This works:
function addData(chart, label, data) {
chart.data.labels = label
chart.data.datasets.forEach((dataset) => {
dataset.data = data;
});
chart.update();
}
$("#btn").click(function() {
addData (myChart, labelsNew, dataNew);
});
instead of pushing the data (which adds on), data needs to be allocated by " = ".
I see 2 problems:
in function updateData() missing chart argument to removeData(chart);
click handler for button, use simply:
$("#btn").click(function() {
updateData(myChart, labelsNew, dataNew)
});
var config = {
type: 'line',
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
data: [65, 0, 80, 81, 56, 85, 40],
fill: false
}]
}
};
var ctx = document.getElementById("myChart").getContext("2d");
var myChart = new Chart(ctx, config);
labelsNew = ["Why", "u", "no", "work", "???"];
dataNew = [2, 4, 5, 6, 10];
function addData(chart, label, data) {
chart.data.labels = label
chart.data.datasets.forEach((dataset) => {
dataset.data = data;
});
chart.update();
}
function clickupdate(){
addData(myChart, labelsNew, dataNew);
}
.chart-container {
height: 300px;
width: 500px;
position: relative;
}
canvas {
position: absolute;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div class="button-container">
<button onclick="clickupdate()">Change Data</button>
</div>
<div class="chart-container">
<canvas id="myChart"></canvas>
</div>

Categories