I'm making a Vertical Bar Chart using react-chartjs-2.
and I have array of lots of float numbers stored.
I tried to use chartjs callback option to display only the last value on a graph.
But the value in the callback is an integer, so I can't take the value I want.
for example,
const xAsisData = [ 1.11, 4.23, 7.34, ... , 403.78 ] // includes hundreds
scales: {
x: {
ticks: {
autoSkip: false,
callback: (value) => {
// if value === last ? value : null
// ! but last value is 309, not 403.78
},
},
Is there any other option I can use?
You get the index and ticks array as well in your callback so you can just check if the index is not the final one like so:
var options = {
type: 'line',
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: {
scales: {
y: {
ticks: {
callback: (val, i, ticks) => (i < ticks.length - 1 ? val : null) // Replace null by empty string to still show the gridLine
}
}
}
}
}
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.3.2/chart.js"></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'm working on a bar chart with multiple stacked datasets using the react wrapper for Chart.js(https://github.com/reactchartjs/react-chartjs-2).
I've been trying to make the chart show only one dataset at a time, but I'm having trouble coming up with the right function.
I want the chart to start off with all the datasets hidden(this part was easy) and when you click the legend I want the chart to only show only the selected dataset. So when you click one and then another I want the first one to get hidden.
I know I need to use the legend onClick option but I can't figure out how to set it up.
Any help would be appreciated, thanks!
Edit: Here is the chart in question: https://codesandbox.io/s/lucid-river-xhvtd?file=/src/charts/bar_chart.js
You can set all datasets to hide and then only the one clicked to show:
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',
hidden: true
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderColor: 'orange',
hidden: true
},
{
label: '# of People',
data: [3, 1, 15, 4, 9, 12],
borderColor: 'cyan',
hidden: true
}
]
},
options: {
plugins: {
legend: {
onClick: (evt, legendItem, legend) => {
const index = legendItem.datasetIndex;
const ci = legend.chart;
legend.chart.data.datasets.forEach((d, i) => {
ci.hide(i);
d.hidden = true;
})
ci.show(index);
legendItem.hidden = false;
ci.update();
}
}
}
}
}
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>
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>
I am trying to find the currently visible data points following a zoom event using chartjs-plugin-zoom. Following examples I came up with the following onZoomComplete callback, but it is not working.
function getVisibleValues({chart}) {
const x = chart.scales.x;
let visible = chart.data.datasets[0].data.slice(x.minIndex, x.maxIndex + 1);
}
One immediate issue is that chart.data doesn't seem to exist (when using console.log(chart.data) it comes back undefined). Same with x.minIndex and x.maxIndex... Any ideas on what I'm doing wrong would be much appreciated.
Below is how I setup the chart (data is an array of x,y pairs):
ctx = new Chart(document.getElementById(ctx_id), {
type: "scatter",
data: {
datasets: [
{
label: "Data",
lineTension: 0,
showLine: true,
data: data,
},
],
},
options: {
animation: false,
plugins: {
zoom: {
zoom: {
mode: "x",
drag: {
enabled: true,
borderColor: "rgb(54, 162, 235)",
borderWidth: 1,
backgroundColor: "rgba(54, 162, 235, 0.3)",
},
onZoomComplete: getVisibleValues,
},
},
},
},
});
You can access the c.chart.scales["x-axis-0"]._startValue and c.chart.scales["x-axis-0"]._valueRange. These two give the first and last visible values respectively.
These values can be used to get the dataset data available at c.chart.config.data.datasets[0].data, or the label names at c.chart.config.data.labels.
If you only need to get the visible tick labels, you can do this by simply accessing the chart.scales["x-axis-0"].ticks object.
function getVisibleValues(c) {
document.getElementById("visibleTicks").textContent = JSON.stringify(
c.chart.scales["x-axis-0"].ticks // This is one way to obtain the visible ticks
);
const start = c.chart.scales["x-axis-0"]._startValue // This is first visible value
const end = start + c.chart.scales["x-axis-0"]._valueRange // This is the last visible value
document.getElementById("visibleValues").textContent = JSON.stringify(
c.chart.config.data.datasets[0].data.slice(start, end + 1) // Access chart datasets
//Note: You can also get the labels from here, these are available at `c.chart.config.data.labels`
);
}
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: "line",
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: "# of Votes",
data: [12, 19, 3, 5, 2, 3]
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
},
plugins: {
zoom: {
zoom: {
// Boolean to enable zooming
enabled: true,
// Zooming directions. Remove the appropriate direction to disable
// Eg. 'y' would only allow zooming in the y direction
mode: "x",
onZoomComplete: getVisibleValues
}
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3/dist/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom#0.7.5/dist/chartjs-plugin-zoom.min.js"></script>
<html>
<body>
Visible ticks: <span id="visibleTicks">Begin zooming</span><br/>Visible data: <span id="visibleValues">Begin zooming</span>
<div class="myChartDiv" style="width: 400px;">
<canvas id="myChart"></canvas>
</div>
</body>
</html>
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>