How to have onclick/hover display associated value in ChartJS - javascript

I have a (regularly updating) ChartJS line chart as follows, working off of three types of data - prices, dates, and associated more_info - collected from a Django API:
<script>
var myChart
function refresh_graph() {
{% block jquery %}
var chart_endpoint = "{% url 'chart_data' current_id %}"
var defaultData = []
var labels = []
var more_info = []
$.ajax({
method: "GET",
url: chart_endpoint,
success: function(data){
defaultData = data.prices
labels = data.dates
more_info = data.more_info
if(myChart){
myChart.destroy();
}
var ctx = document.getElementById('myChart').getContext('2d');
myChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets : [{
label: 'Price',
data: defaultData,
backgroundColor: [
'rgba(54, 162, 235, 0.2)',
],
borderColor: [
'rgba(54, 162, 235, 1)',
],
borderWidth: 2
}]
},
options: {
elements: {
line: {
tension: 0 // disables bezier curves
}
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
suggestedMin: 0,
suggestedMax: 1
}
}]
},
animation: {
duration: 0 // general animation time
},
hover: {
animationDuration: 0 // duration of animations when hovering an item
},
responsiveAnimationDuration: 0 // animation duration after a resize
}
})
}
})
setTimeout(refresh_graph, 5000);
{% endblock %}
}
setTimeout(refresh_graph, 0);
</script>
<div>
<canvas id="myChart" width="1000" height="400"></canvas>
</div>
I'm trying to figure out how to make it so that, when the user clicks on or hovers over (I'd be happy with either) one of the data points in the graph (i.e., a price at a date), they'll see the associated more_info.
I'm aware of this using getElementById, but can't figure out how to extend that to a case like this, where I'm not looking to display a label (here: date) and a data point value (here: price), but rather a third value, i.e., more_info.
I'm also aware of this way of using custom tooltips, but also can't figure out how to extend this from the case where I'm simply using tooltipItem.xLabel (date) and tooltipItem.yLabel (price), as opposed to a third, associated value.

Here's a solution using tooltips (ignoring the Ajax from the OP):
var myChart
function refresh_graph() {
var labels = ["Monday", "Tuesday", "Wednesday","Thursday","Friday","Saturday","Sunday"]
var defaultData = [0.1,0.5,0.3,0.4,0.6,0.8,0.3]
var more_info = ["Monday info", "Tuesday info", "Wednesday info","Thursday info","Friday info","Saturday info","Sunday info"]
if(myChart){
myChart.destroy();
}
var ctx = document.getElementById('myChart').getContext('2d');
myChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: "Price",
data: defaultData,
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 2,
}]
},
options: {
responsive : true,
tooltips : {
callbacks : {
title : function() {
return 'More information:';
},
afterLabel : function(tooltipItem, data) {
return 'Information: ' + more_info[tooltipItem.index];
},
}
},
elements: {
line: {
tension: 0
}
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
suggestedMin: 0,
suggestedMax: 1
}
}]
},
animation: {
duration: 0
},
hover: {
animationDuration: 0
},
responsiveAnimationDuration: 0
}
})
setTimeout(refresh_graph, 50000);
}
setTimeout(refresh_graph, 0);
Full codepen with custom tooltip here: https://codepen.io/kh_one/pen/OJJPBpJ.

Related

How to setup or show all tool-tip text by default without hover

I am working on one project where i am using Charts.js for showing charts, Here i want to show all bar values by default on top of each bar (Using bar chat), when we hover mouse on that bar it shows value, but is there any was to show that tooltip by default without hover on it
Below fiddle is the example, Here in this fiddle i want to show 60,30,40 as default on bar chart by default without taking hover on it.
Here in above screenshot i have tried to put all the tooltip as by default but as there are 3 charts are used it shows tooltip to all the charts but I want to put tooltip to only bar chart, I know it's bit tricky but there will be some way using which i can resolve my issue.
Please guide me, here is my code snipped of JS
getchart();
function getchart() {
$("#barChart").remove();
const decimals = 3;
$(".chart").append("<canvas id='barChart' width='692' height='346'></canvas>");
var ctx = document.getElementById("barChart");
Chart.pluginService.register({
beforeRender: function(chart) {
if (chart.config.options.showAllTooltips) {
chart.pluginTooltips = [];
chart.config.data.datasets.forEach(function(dataset, i) {
chart.getDatasetMeta(i).data.forEach(function(sector, j) {
console.log(chart);
chart.pluginTooltips.push(new Chart.Tooltip({
_chart: chart.chart,
_chartInstance: chart,
_data: chart.data,
_options: chart.options.tooltips,
_active: [sector]
}, chart));
});
});
}
chart.options.tooltips.enabled = false;
},
afterDraw: function(chart, easing) {
if (chart.config.options.showAllTooltips) {
if (!chart.allTooltipsOnce) {
if (easing !== 1)
return;
chart.allTooltipsOnce = true;
}
chart.options.tooltips.enabled = true;
Chart.helpers.each(chart.pluginTooltips, function(tooltip) {
tooltip.initialize();
tooltip.update();
tooltip.pivot();
tooltip.transition(easing).draw();
});
chart.options.tooltips.enabled = false;
}
}
})
var barChart;
barChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['30', '45', '60', '90', '120', '120+'],
datasets: [{
type: 'bar',
label: 'Receivable',
data: [730, 492.5, 120, 4732.5, 2760.85, 0],
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}, {
type: 'line',
label: 'Past Due',
data: [1500, 1500, 1500, 1500, 1500, 1500],
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255,99,132,1)',
borderWidth: 1
}, {
type: 'line',
label: 'Invoice',
data: [1000, 1000, 1000, 1000, 1000, 1000],
backgroundColor: 'rgba(75, 00, 150, 0.2)',
borderColor: 'rgba(75, 00, 150,1)',
borderWidth: 2
}]
},
options: {
scales: {
xAxes: [{
display: true,
stacked: true,
scaleLabel: {
display: true,
labelString: 'Days'
},
}],
yAxes: [{
display: true,
stacked: true,
scaleLabel: {
display: true,
labelString: 'Dollar Amount'
},
ticks: {
beginAtZero: true,
}
}, {
id: 'invoice-amount',
display: false,
stacked: false,
scaleLabel: {
display: false,
labelString: 'Dollar Amount'
},
ticks: {
beginAtZero: true,
}
}]
},
showAllTooltips: true,
}
});
}

Chartjs Line Chart showing old data when hovering

I have a line chart that's created using chart.js. Everything works fine on page load, but when toggle the chart and hover to chart's corners, it shows old charts.
Also here is the live webapp that you can see the behavior.
https://www.batchrank.com/
I tried to fix with update(), destroy and clear however non of them worked.
Thanks a lot for the help,
Here is my code;
var ctx = document.getElementById("chartContainer").getContext("2d");
var myChart = new Chart(ctx, {
type: "line",
data: {
label: "data",
datasets: [
{
label: "Mobile Speed Scores",
data: mobileData,
backgroundColor: ["rgba(255, 99, 132, 0.2)"],
borderColor: ["rgba(255, 99, 132, 1)"],
borderWidth: 1
},
{
label: "Desktop Speed Scores",
data: desktopData,
backgroundColor: ["rgba(49, 67, 129, 0.2)"],
borderColor: ["rgba(49, 67, 129, 1)"],
borderWidth: 1
}
]
},
options: {
responsive: true,
title: {
display: true,
text: inspectUrl
},
scales: {
xAxes: [
{
type: "time",
display: true,
scaleLabel: {
display: true,
labelString: "Date"
}
}
],
yAxes: [
{
scaleLabel: {
display: true,
labelString: "value"
}
}
]
}
}
});
myChart.render();
const removeElements = elms => elms.forEach(el => el.remove());
removeElements(document.querySelectorAll(".progress"));
function toogleDataSeries(e) {
e.dataSeries.visible = !(
typeof e.dataSeries.visible === "undefined" || e.dataSeries.visible
);
myChart.destroy();
myChart.render();
}
}

How to make a chart with an associative array with chart.js?

I have an app in PHP using Laravel and I've got two arrays I have to join in a single chart. Both show a quantity per month. I could write a single label as the documentation shows for a normal use case but both lines don't necessarily include the same amount of months so I'd like to associate each y axis figure to its specific month label.
var ctx = document.getElementById('flujomes');
var entregas = #json($ar_entregas);
var reversas = #json($ar_reversas);
console.log(entregas);
console.log(reversas);
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: "Número de entregas",
data: entregas,
backgroundColor: [
'rgba(255, 99, 132, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
],
borderWidth: 1
},
{
label: "Número de reversas",
data: reversas,
backgroundColor: [
'rgba(54, 162, 235, 0.2)'
],
borderColor: [
'rgba(54, 162, 235, 1)'
],
borderWidth: 1
}
]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
},
responsive: true,
maintainAspectRatio: false,
}
});
However it doesn't work as I expected:
So chart.js picks up the data but it doesn't know where x points actually are.
The problem is that the library doesn't know who to handle the x values that you are giving, only if you are using a linear axis (numerical data) it will be able to do this correctly without other options.
There are two ways to solve this,
Option 1 - Define a category axis
Since you know the values that will be on the labels you can set them with the type: 'category' , so library knows where to put the points correctly like the following example:
var chart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: "Data 1",
data: [{
x: '2018-2',
y: 98
}, {
x: '2018-4',
y: 74
}, {
x: '2018-5',
y: 52
}],
}, {
label: "Data 2",
data: [{
x: '2018-3',
y: 25
}, {
x: '2018-5',
y: 52
}],
}]
},
options: {
scales: {
xAxes: [{
type: 'category',
labels: ['2018-2', '2018-3', '2018-4', '2018-5']
}]
},
tooltips: {
mode: 'x',
callbacks: {
title: function(tooltipItems, data) {
let tI = tooltipItems[0];
return data.datasets[tI.datasetIndex].data[tI.index].x;
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.0/Chart.min.js"></script>
<canvas id="ctx"></canvas>
Option 2 - Transform the labels into dates
Another possible solution will be to transform the labels from text into Date() with moment like moment("2018-2") and then set a min and max as ticks value, and also the display format, with something like:
xAxes: [{
type: 'time',
time: {
displayFormats: {
quarter: 'YYYY-MM'
}
}
}]
This is in overall a more complex solution.
Update - fix, wrong tooltip title
As mention the first solution will lead to when a user hover a point the title of the tooltip will be different from the x label, that is because that point carries a wrong index value (this index is used to return the label position).
In attempting to solve this, and since the Scatter charts create a similar result with what we are trying to achieve, I have locked up at the controller of this type here, but without luck, because in this situations or the title is ignored or the value is also wrong.
So what I came up with is something like this:
tooltips: {
mode: 'x', // optional
callbacks: {
title: function(tooltipItems, data) {
let tI = tooltipItems[0];
return data.datasets[tI.datasetIndex].data[tI.index].x;
}
}
}
Basically instead of searching the index in the labels array, it will get the original x data value.

How to edit my custom tooltips in my line chart using chart.js?

This is my code
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: [{{ $nDate }}],
datasets: [{
label: 'All Users',
data: [ {{ $nUser }} ],
backgroundColor: ['rgba(54, 162, 235, 0.2)'],
borderColor: ['rgba(54, 162, 235, 1)'],
borderWidth: 3,
lineTension: 0,
labelFontSize : '16',
}]
},
options: {
tooltips: {
mode: 'index',
intersect: false,
position: 'nearest'
},
responsive: false,
legend: { display: false },
scales: {
yAxes: [{
ticks: {
beginAtZero:true,
type: 'category',
labelOffset: 10,
stepSize:250,
callback: function(value, index) {
if (value !== 0) return value;
}
},
gridLines:{
drawBorder:false
}
}],
xAxes: [{
gridLines: {
display: false,
},
}],
},
plugins:{
datalabels:{
display:false
}
}
}
});
My output
My Expected Output
How can I able to put/edit custom tooltip in my line chart? I just want to get the exact tooltips in the second picture but I don't have any idea how to fix it? Another thing is my $nDateI only want to show four dates like 8,15,22,29 But when I tried to create a new array label with value of this [" ", " "]; my chart crashed.
You can use custom callback function to render with your own choice html tags and colors, Follow the official documentation link for further guidance.
http://www.chartjs.org/docs/latest/configuration/tooltip.html#external-custom-tooltips
options: {
tooltips: {
enabled: false,
custom: function(tooltipModel) {}
}
}

How to add images as labels to Canvas Charts using chart.js

I am generating a chart.js canvas bar chart. What I am trying to do is, inside of the labels array, add images that go with each label, as opposed to just the text label itself. Here is the code for the chart: The json object that I am getting data from has an image url that I want to use to display the picture:
$.ajax({
method: "get",
url: "http://localhost:3000/admin/stats/show",
dataType: "json",
error: function() {
console.log("Sorry, something went wrong");
},
success: function(response) {
console.log(response)
var objectToUse = response.top_dogs
var updateLabels = [];
var updateData = [];
for (var i = 0; i < objectToUse.length; i+=1) {
updateData.push(objectToUse[i].win_percentage * 100);
updateLabels.push(objectToUse[i].title);
}
var data = {
labels: updateLabels,
datasets: [
{
label: "Top Winners Overall",
fillColor: get_random_color(),
strokeColor: "rgba(220,220,220,0.8)",
highlightFill: get_random_color(),
highlightStroke: "rgba(220,220,220,1)",
data: updateData
}
]
};
var options = {
//Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
scaleBeginAtZero : true,
//Boolean - Whether grid lines are shown across the chart
scaleShowGridLines : true,
//String - Colour of the grid lines
scaleGridLineColor : "rgba(0,0,0,.05)",
//Number - Width of the grid lines
scaleGridLineWidth : 1,
//Boolean - Whether to show horizontal lines (except X axis)
scaleShowHorizontalLines: true,
//Boolean - Whether to show vertical lines (except Y axis)
scaleShowVerticalLines: true,
//Boolean - If there is a stroke on each bar
barShowStroke : true,
//Number - Pixel width of the bar stroke
barStrokeWidth : 2,
//Number - Spacing between each of the X value sets
barValueSpacing : 5,
//Number - Spacing between data sets within X values
barDatasetSpacing : 2,
};
var loadNewChart = new Chart(barChart).Bar(data, options);
}
});
If anyone has a solution it would be greatly appreciated!
I'm aware that this is an old post but since it has been viewed so many times, I'll describe a solution that works with the current Chart.js version 2.9.3.
The Plugin Core API offers a range of hooks that may be used for performing custom code. You can use the afterDraw hook to draw images (icons) directly on the canvas using CanvasRenderingContext2D.
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
xAxis.ticks.forEach((value, index) => {
var x = xAxis.getPixelForTick(index);
ctx.drawImage(images[index], x - 12, yAxis.bottom + 10);
});
}
}],
The position of the labels will have to be defined through the xAxes.ticks.padding as follows:
xAxes: [{
ticks: {
padding: 30
}
}],
Please have a look at the following runnable code snippet.
const labels = ['Red Vans', 'Blue Vans', 'Green Vans', 'Gray Vans'];
const images = ['https://i.stack.imgur.com/2RAv2.png', 'https://i.stack.imgur.com/Tq5DA.png', 'https://i.stack.imgur.com/3KRtW.png', 'https://i.stack.imgur.com/iLyVi.png']
.map(png => {
const image = new Image();
image.src = png;
return image;
});
const values = [48, 56, 33, 44];
new Chart(document.getElementById("myChart"), {
type: "bar",
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
xAxis.ticks.forEach((value, index) => {
var x = xAxis.getPixelForTick(index);
ctx.drawImage(images[index], x - 12, yAxis.bottom + 10);
});
}
}],
data: {
labels: labels,
datasets: [{
label: 'My Dataset',
data: values,
backgroundColor: ['red', 'blue', 'green', 'lightgray']
}]
},
options: {
responsive: true,
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}],
xAxes: [{
ticks: {
padding: 30
}
}],
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="90"></canvas>
Chart.js v3+ solution to pie, doughnut and polar charts
With version 3 of Chart.js and the updated version of chart.js-plugin-labels, this is now incredbly simple.
in options.plugins.labels, add render: image and the nested array images with objects containing the properties src, width and height.
const data = {
labels: ['Label 1', 'Label 2', 'Label 3', 'Label 4', 'Label 5', 'Label 6', 'Label 7', 'Label 8'],
datasets: [{
label: 'Image labels',
// Making each element take up full width, equally divided
data: [100, 100, 100, 100, 100, 100, 100, 100],
backgroundColor: [
'rgba(255, 26, 104, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)',
'rgba(0, 0, 0, 0.2)',
'rgba(20, 43, 152, 0.2)'
]
}]
};
const config = {
type: 'doughnut',
data,
options: {
plugins: {
// Accessing labels and making them images
labels: {
render: 'image',
images: [{
src: 'https://cdn0.iconfinder.com/data/icons/google-material-design-3-0/48/ic_book_48px-256.png',
height: 25,
width: 25
},
{
src: 'https://cdn3.iconfinder.com/data/icons/glypho-free/64/pen-checkbox-256.png',
height: 25,
width: 25
},
{
src: 'https://cdn1.iconfinder.com/data/icons/jumpicon-basic-ui-glyph-1/32/-_Home-House--256.png',
height: 25,
width: 25
},
{
src: 'https://cdn1.iconfinder.com/data/icons/social-media-vol-3/24/_google_chrome-256.png',
height: 25,
width: 25
},
{
src: 'https://cdn0.iconfinder.com/data/icons/google-material-design-3-0/48/ic_book_48px-256.png',
height: 25,
width: 25
},
{
src: 'https://cdn3.iconfinder.com/data/icons/glypho-free/64/pen-checkbox-256.png',
height: 25,
width: 25
},
{
src: 'https://cdn1.iconfinder.com/data/icons/jumpicon-basic-ui-glyph-1/32/-_Home-House--256.png',
height: 25,
width: 25
},
{
src: 'https://cdn1.iconfinder.com/data/icons/social-media-vol-3/24/_google_chrome-256.png',
height: 25,
width: 25
},
]
}
}
}
};
// render init block
const myChart = new Chart(
document.getElementById('myChart').getContext('2d'),
config
);
.chartCard {
width: 100vw;
height: 500px;
display: flex;
align-items: center;
justify-content: center;
}
.chartBox {
width: 600px;
padding: 20px;
}
<div class="chartCard">
<div class="chartBox">
<canvas id="myChart"></canvas>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://unpkg.com/chart.js-plugin-labels-dv#3.0.5/dist/chartjs-plugin-labels.min.js"></script>

Categories

Resources