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>
Related
I am using chart.js to develop a TreeMap. It works great but the issue is each rectangle only shows the value of the data. I want to add some custom text next to the value. How do I achieve this?
var topTags = [
{tag:'python',num:133269},{tag:'javascript',num:109742},{tag:'java',num:65490},{tag:'c#',num:45445},{tag:'html',num:43767},{tag:'android',num:42657},{tag:'reactjs',num:38844},{tag:'php',num:34381},{tag:'sql',num:29996},{tag:'python',num:29942},{tag:'css',num:29654},{tag:'r',num:28319},{tag:'c++',num:27389},{tag:'node.js',num:25415},{tag:'angular',num:24093},{tag:'pandas',num:23826},{tag:'arrays',num:23075},{tag:'jquery',num:18968},{tag:'mysql',num:18863},{tag:'swift',num:17971},{tag:'ios',num:17600},{tag:'json',num:15589},{tag:'laravel',num:15537},{tag:'flutter',num:15201},{tag:'c',num:15195},{tag:'django',num:14748},{tag:'sql',num:12752},{tag:'reactjs',num:10974},{tag:'excel',num:10962},{tag:'list',num:10726},{tag:'regex',num:10676}
];
var canvas = document.getElementById("treemap");
var ctx = canvas.getContext("2d");
var chart = window.chart = new Chart(ctx, {
type: "treemap",
data: {
datasets: [{
tree: topTags,
key: "num",
groups: ['num'],
spacing: 0.5,
borderWidth: 1.5,
fontColor: "black",
borderColor: "grey"
}]
},
options: {
maintainAspectRatio: false,
legend: { display: false },
tooltips: { enabled: false }
}
});
When running this code, I get the numbers in each rectangle but I need some text to be appended next to the numbers for each rectangle. How do I achieve this?
You can use the formatter options in the labels section:
const options = {
type: 'treemap',
data: {
datasets: [{
label: '# of Votes',
tree: [12, 19, 3, 5, 2, 3],
backgroundColor: 'pink',
labels: {
display: true,
formatter(ctx) {
const data = ctx.chart.data;
return `Custom Text: ${data.datasets[ctx.datasetIndex].tree[ctx.dataIndex]}`;
}
},
}]
},
options: {}
}
const 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.7.0/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-chart-treemap#2.0.1/dist/chartjs-chart-treemap.js"></script>
</body>
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;
});
}
}
}
}
In Chart.js v2, the datasetIndex property of ChartTooltipItem[] identifies which segment of a stacked bar chart was clicked. This allowed the tooltip content to be customized for each segment of the stacked bar chart.
In v3, TooltipItem[] provides the datasets but does not identify which one was clicked. There is a datasetIndex field for each TooltipItem, but it just matches the index in TooltipItem[] rather than identify the clicked segment.
Has anyone found a field in the V3 tooltip callback to identified which segment of a stacked bar chart was clicked? Or was this functionality lost in the v3 rewrite?
It just works fine, the only thing that is different is that it seems like in v2 it defaulted to the point mode while now it is using index mode, if you change it back to point it works as expected:
var options = {
type: 'bar',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: 'red'
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
backgroundColor: 'blue'
}
]
},
options: {
plugins: {
tooltip: {
mode: 'point',
callbacks: {
beforeBody: (ttItems, x) => {
ttItems.forEach((ttItem) => {
console.log('BeforeBody: ', ttItem.datasetIndex, ttItem.dataIndex)
})
},
afterBody: (ttItems, x) => {
ttItems.forEach((ttItem) => {
console.log('AfterBody: ', ttItem.datasetIndex, ttItem.dataIndex)
})
}
}
}
},
scales: {
y: {
stacked: true
},
x: {
stacked: true
}
}
}
}
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.4.1/chart.js"></script>
</body>
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>