I have a flask app that utilizes chart.js,
live app can viewed here: https://flaskapp.gullp.repl.co/
I'm trying to pull the clicked value from the chart and put it into a variable that resides outside of the chart function.
Here is my chart logic:
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: /* {{ labels }}*/ ['Big Home', 'Medium Home', 'Small Home'] ,
datasets: [{
label: 'count per label',
data: /*{{ values }} */ [3,2,1]
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
//below allows you to click the chart and get the respective value. you will pass this value to python
,onClick: function(c,i) {
e = i[0];
//console.log(e._index)
var x_value = this.data.labels[e._index];
var y_value = this.data.datasets[0].data[e._index];
// console.log(x_value);
//console.log(y_value);
console.log('you clicked the graph, now the x value is = ' + x_value)
console.log('since you clicked, this is the chart clicked data = ' + JSON.stringify(chart_clicked_data) )
document.querySelectorAll("tr").forEach((tr, i) => {
if(i === 0){
// skip column names
return
}
if(tr.children[0].innerText != x_value){
tr.style.display = 'none';
}else {
tr.style.display = 'table-row';
}
})
}
}
});
Here is my code trying to extract the x-value:
var chart_clicked_data = { 'score' : myChart.options.x_value}
console.log('this is the json chart clicked data that we want to pass back to flask = ' + JSON.stringify(chart_clicked_data) )
seems like this is not doing the trick:
myChart.config.options.x_value
when i check the console after i click the value, its not showing to be picking up.
entire live code here:
https://repl.it/join/rbkobiqi-gullp
Add an variable outside of your chartFunctions, set that variable in the click handler and then use it
let xVal = ''
var options = {
type: 'bar',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderWidth: 1
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderWidth: 1
}
]
},
options: {
onClick: function(c, i) {
e = i[0];
//console.log(e._index)
var x_value = this.data.labels[e._index];
var y_value = this.data.datasets[0].data[e._index];
// console.log(x_value);
//console.log(y_value);
console.log('you clicked the graph, now the x value is = ' + x_value)
xVal = x_value
},
scales: {
yAxes: [{
ticks: {
reverse: false
}
}]
}
}
}
document.getElementById('temp').addEventListener('click', () => {
console.log('X val: ', xVal);
alert('X val: ' + xVal)
})
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
canvas {
background-color: #eee;
}
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<button id="temp">
show pressed X value
</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js" integrity="sha512-hZf9Qhp3rlDJBvAKvmiG+goaaKRZA6LKUO35oK6EsM0/kjPK32Yw7URqrq3Q+Nvbbt8Usss+IekL7CRn83dYmw==" crossorigin="anonymous"></script>
</body>
Related
So I'm trying to make a JS function that uses ChartJS, the variable/data the fucntion gets, is a JSON object. The reason why I use try/catch is because the two lines under here:
let labels = json_data.data.map(e => e.StartTime);
let data = json_data.data.map(e => e.StatusId);
Isn't always set if there is any data they are set, but if not is the only one set
let data = json_data.message.map(e => e.message);
Unless on page load, then nothing is set.
The JSON object change when a <Select> dropdown is changed, and if there is data in one then the canvas is loaded, but if the user then select one with no data, I want the graft to be empty/destroyed, but I cant do this because I'm in a try catch, and if I define it in the catch too, then it says that the ID already is in use. What do I have to do to "reset/destroy" it in the catch?
function chartJSLoad(json_data) {
try {
let labels = json_data.data.map(e => e.StartTime);
let data = json_data.data.map(e => e.StatusId);
console.log(labels);
console.log(data);
var ctx = document.getElementById('canvaschartjs').getContext('2d');
var myChart = new Chart(ctx, {
data: {
datasets: [{
type: 'bar',
label: 'Bar Dataset',
data: data,
backgroundColor: 'rgba(0, 150, 90)',
borderColor: 'rgba(0, 255, 90)',
order: 2
}, {
type: 'line',
label: 'Line Dataset',
data: data,
backgroundColor: 'rgba(150, 0, 90)',
borderColor: 'rgba(255, 0, 90)',
order: 1
}],
labels: labels
},
options: {
maintainAspectRatio: false,
responsive: true,
scales: {
x: {
stacked: true,
},
y: {
stacked: true
}
}
}
});
} catch (error) {
if (typeof json_data !== 'undefined') {
myChart.destroy();
alert(json_data.message);
}
}
}
You can use the static method getChart to check if a chart with that context already exists, if it does you get that chart instance which you can destroy:
catch (error) {
if (typeof json_data !== 'undefined') {
let chart = Chart.getChart('canvaschartjs');
if (typeof chart !== 'undefined') {
chart.destroy()
}
alert(json_data.message);
}
}
Live example:
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'pink'
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderColor: 'orange'
}
]
},
options: {}
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
let myChart = new Chart(ctx, options);
let chart = Chart.getChart('chartJSContainer');
if (typeof chart !== 'undefined') {
chart.destroy() // Does not show anything because of this line, comment it out to show again
}
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.0/chart.js"></script>
</body>
I need the value of chart show after name of data for example ([colour of data] Car 50, [colour of data] Motorcycle 200). I've tried change the value of legend title but it doesn't work at all
Here is it my code:
var ctx = document.getElementById('top-five').getContext('2d');
var myChartpie = new Chart(ctx, {
type: 'pie',
data: {
labels: {!! $top->pluck('name') !!},
datasets: [{
label: 'Statistics',
data: {!! $top->pluck('m_count') !!},
backgroundColor: {!! $top->pluck('colour') !!},
borderColor: {!! $top->pluck('colour') !!},
}]
},
options: {
plugins: {
legend: {
display: true,
title: {
text: function(context) {//I've tried to override this but doesn't work
var value = context.dataset.data[context.dataIndex];
var label = context.label[context.dataIndex];
return label + ' ' + value;
},
}
},
},
responsive: true,
}
});
You can use a custom generateLabels function for this:
var options = {
type: 'doughnut',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
}]
},
options: {
plugins: {
legend: {
labels: {
generateLabels: (chart) => {
const datasets = chart.data.datasets;
return datasets[0].data.map((data, i) => ({
text: `${chart.data.labels[i]} ${data}`,
fillStyle: datasets[0].backgroundColor[i],
index: i
}))
}
}
}
}
}
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.js"></script>
</body>
The below is a direct over ride of the default label generation found in the controller here. I have made one change on the text property within the generateLabels function in order to append the data value. It preserves the data toggling and strikethrough styling when a label is toggled.
plugins: {
legend: {
labels: {
generateLabels(chart) {
const data = chart.data;
if (data.labels.length && data.datasets.length) {
const {labels: {pointStyle}} = chart.legend.options;
return data.labels.map((label, i) => {
const meta = chart.getDatasetMeta(0);
const style = meta.controller.getStyle(i);
return {
text: `${label}: ${data['datasets'][0].data[i]}`,
fillStyle: style.backgroundColor,
strokeStyle: style.borderColor,
lineWidth: style.borderWidth,
pointStyle: pointStyle,
hidden: !chart.getDataVisibility(i),
// Extra data used for toggling the correct item
index: i
};
});
}
return [];
}
},
onClick(e, legendItem, legend) {
legend.chart.toggleDataVisibility(legendItem.index);
legend.chart.update();
}
}
//...
}
[1]: https://github.com/chartjs/Chart.js/blob/master/docs/samples/legend/html.md
You can also use the base implementation to reduce the amount of copied code. Note that some chart types (like donut) already overrides the default label generation.
plugins: {
legend: {
labels: {
generateLabels: function (chart) {
return Chart.defaults.plugins.legend.labels.generateLabels(chart).map(function (label) {
var dataset = chart.data.datasets[label.datasetIndex];
var total = 0;
for (var j = 0; j < dataset.data.length; j++)
total += dataset.data[j].y;
label.text = dataset.label + ': ' + total;
return label;
});
}
}
}
}
I'm using Chart JS version 3.5.1, and I can't get the onHover function to work. My goal is to have the user hover or a data point, and then have the dataset values available to me inside the onHover.
Here's my setup:
var context = document.getElementById('canvas').getContext('2d')
var options = {
onHover: function(evt) {
console.log(evt)
var item = myChart.getElementAtEvent(evt)
console.log(item)
},
interaction: {
mode: 'index',
intersect: false,
},
plugins: {
legend: {
display: false,
}
},
responsive: true,
maintainAspectRatio: false
}
var myChart = new Chart(context, {
type: 'line',
data: {
labels: obj.labels,
datasets: [...]
},
options: options
})
I'm getting and error on this line:
var item = myChart.getElementAtEvent(evt)
The error says:
myChart.getElementAtEvent is not a function
I have confirmed that myChart is an object, but there doesn't appear to be a getElementAtEvent function.
I also tried the example in the docs here:
const canvasPosition = Chart.helpers.getRelativePosition(e, chart);
// Substitute the appropriate scale IDs
const dataX = chart.scales.x.getValueForPixel(canvasPosition.x);
const dataY = chart.scales.y.getValueForPixel(canvasPosition.y);
But it says:
Cannot read property 'getValueForPixel' of undefined
Does anyone know how I can successfully use onHover with version 3.5.1 of Chart JS?
You are using chart.js v3, in v3 there is no getElementAtEvent, it has been replaced with getElementsAtEventForMode. You can use this to get the active elements but you can also use the second argument the onHover function recieves which is an array with all the active elements:
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'pink'
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderColor: 'orange'
}
]
},
options: {
onHover: (evt, activeEls, chart) => {
if (activeEls.length === 0 || chart.getElementsAtEventForMode(evt, 'nearest', {
intersect: true
}, true).length === 0) {
return;
}
console.log('Function param: ', activeEls[0].index);
console.log('lookup with the event: ', chart.getElementsAtEventForMode(evt, 'nearest', {
intersect: true
}, true)[0].index);
activeEls.forEach(point => {
console.log('val: ', chart.data.datasets[point.datasetIndex].data[point.index])
})
}
}
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.js"></script>
</body>
Good day to all:
Recently I have started working with Vue.js(2.6.12) + Vuetify(2.3.10) and Chart.js(3.0.2). So I'm a newbie (again).
I have created a componenent which wraps the Bar chart that Chart.js allows us to create. Here it's a picture of it:
The only two things that I want to change are the little box that comes near to the legend title and some grid x lines.
In the case of the little legend box is red. I would like that It's aligned with the blue color of the legend title text. As I show you in this picture:
Finally I want to include some color (pink) in the X axis:
I'm unable to achieve this. I have regarded and follow the official documentation and nothing works :S.
Create the function for painting some axis in colors: https://www.chartjs.org/docs/3.0.2/samples/scale-options/grid.html
Change the colors of the legend: https://www.chartjs.org/docs/3.0.2/configuration/legend.html#legend-label-configuration
In the case of the legend title box I have noticed that always takes the color of the first element.
In the case of the axis, the function supported by chart.js doesn't work for me. It doesn't print me the x axis at all.
Things that I have tried:
Upgrade to chart.js 3.3.0 but I got an error like this: ""
The reason I was using 3.0.2 it's because it's the only version which
is working to me from the 3.0.0 versions.
Downgrade to 2.9.3/4. I wasn't able to change the color of the box or the axis lines, but the rest worked fine.
Use a wrapper: https://vue-chartjs.org/. It didn't work
Code of the whole component:
<template>
<div class="container pa-3" fill-height fluid style="width: 100%">
<!-- We create the chart -->
<canvas id="myChart1" />
</div>
</template>
<script>
import Chart from "chart.js/auto";
export default {
name: "Chart",
components: {},
props: {},
data: () => ({
ctx: null,
myChart: null,
type: "bar",
data: {
labels: ["a", "b", "c", "d"],
datasets: [
{
data: [1, 2, 3, 4],
backgroundColor: ["#c30", "#e37609", "#ffda05", "#fffb05"],
},
],
},
options: {
plugins: {
legend: {
display: true,
labels: {
color: "#00a3fb",
},
},
},
scales: {
},
},
}),
methods: {
createChart: function () {
// destroy the previous graph
if (this.myChart != null) this.myChart.destroy();
// create a new one
this.ctx = document.getElementById("myChart1");
this.myChart = new Chart(this.ctx, {
type: this.type,
data: this.data,
options: this.options,
});
this.myChart.render();
},
},
destroyed() {},
mounted() {
this.createChart();
},
watch: {},
};
</script>
<style scoped>
</style>
For using it, you should:
Import it in the section
Declare it in the component section
Call it by <NameOfComponetGiven/> tag
Any help would be quite aprecciated.
Thank you very much.
To customize the legend box color you will need to use a custom HTML legend, there you can specify it with CSS, for the pink grid lines you can use the scriptable options. For both see example:
const getOrCreateLegendList = (chart, id) => {
const legendContainer = document.getElementById(id);
let listContainer = legendContainer.querySelector('ul');
if (!listContainer) {
listContainer = document.createElement('ul');
listContainer.style.display = 'flex';
listContainer.style.flexDirection = 'row';
listContainer.style.margin = 0;
listContainer.style.padding = 0;
legendContainer.appendChild(listContainer);
}
return listContainer;
};
const htmlLegendPlugin = {
id: 'htmlLegend',
afterUpdate(chart, args, options) {
const ul = getOrCreateLegendList(chart, options.containerID);
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = chart.options.plugins.legend.labels.generateLabels(chart);
items.forEach(item => {
const li = document.createElement('li');
li.style.alignItems = 'center';
li.style.cursor = 'pointer';
li.style.display = 'flex';
li.style.flexDirection = 'row';
li.style.marginLeft = '10px';
li.onclick = () => {
const {
type
} = chart.config;
if (type === 'pie' || type === 'doughnut') {
// Pie and doughnut charts only have a single dataset and visibility is per item
chart.toggleDataVisibility(item.index);
} else {
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
}
chart.update();
};
// Color box
const boxSpan = document.createElement('span');
boxSpan.style.background = options.legendBoxColor || item.fillStyle;
boxSpan.style.borderColor = item.strokeStyle;
boxSpan.style.borderWidth = item.lineWidth + 'px';
boxSpan.style.display = 'inline-block';
boxSpan.style.height = '20px';
boxSpan.style.marginRight = '10px';
boxSpan.style.width = '20px';
// Text
const textContainer = document.createElement('p');
textContainer.style.color = options.legendTextColor || item.fontColor;
textContainer.style.margin = 0;
textContainer.style.padding = 0;
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
const text = document.createTextNode(item.text);
textContainer.appendChild(text);
li.appendChild(boxSpan);
li.appendChild(textContainer);
ul.appendChild(li);
});
}
};
const options = {
type: 'bar',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderWidth: 1,
backgroundColor: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"]
}]
},
options: {
scales: {
x: {
grid: {
color: (line) => ((line.index === 2 || line.index === 3) ? 'pink' : 'rgba(0,0,0,0.1)'),
lineWidth: (line) => ((line.index === 2 || line.index === 3) ? 6 : 1)
}
}
},
plugins: {
htmlLegend: {
// ID of the container to put the legend in
containerID: 'legendContainer',
legendBoxColor: 'blue',
legendTextColor: 'blue'
},
legend: {
display: false,
}
}
},
plugins: [htmlLegendPlugin]
}
const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<div id="legendContainer"></div>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.0.2/chart.js"></script>
</body>
I have successfully displayed the value, but why is only one value displayed? I want the value in sequence
This my code
/*my datasets code*/
datasets: [{
label: 'Daily Data',
data: [730000, 1012000, 1220000, 1831000, 560000, 2012000, 890000],
borderColor: '#3f89fb',
borderWidth: 3,
fill: false
}]
/*my tooltips code*/
tooltips: {
callbacks: {
label: function(tooltipItem, chart) {
for (var i = 0; i < chart.datasets[0].data.length; i++) {
return chart.datasets[0].data[i] / 1e6 + 'M';
}
}
}
}
and this my result, all day value is 0.73M
Look in the Tooltip Item Documentation.
In your case tooltipItem.index contains the index of this data item in the dataset. So you can return the value doing so:
function(tooltipItem, chart) {
return chart.datasets[0].data[tooltipItem.index] / 1e6 + 'M';
}
And here is the demo:
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: 'Daily Data',
data: [730000, 1012000, 1220000, 1831000, 560000, 2012000, 890000],
borderColor: '#3f89fb',
borderWidth: 3,
fill: false
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
},
tooltips: {
callbacks: {
label: function(tooltipItem, chart) {
return chart.datasets[0].data[tooltipItem.index] / 1e6 + 'M';
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<canvas id="myChart" width="400" height="400"></canvas>
Instead of return inside for loop which will exit the loop, you can save your results somewhere like below
var result = []
for (var i = 0; i < chart.datasets[0].data.length; i++) {
result.push(chart.datasets[0].data[i] / 1e6 + 'M');
}
This is because return stops execution and exits the function. return always exits its function immediately, with no further execution if it's inside a loop.
See this answer:
Does return stop a loop?