How can according to my status show only a few tooltip forever - chart.js

I have some data in data. I have seen examples that show all tooltips forever, but this is not what I want. I want to show them forever according to the value of status. If the value of status is null, it will not show. I should How to do it and only car_number according to status show a few tooltip
<div>
<canvas id="chart" style="width: 600px;height: 300px;"></canvas>
<script>
const chart = document.querySelector('#chart').getContext('2d');
new Chart(chart, {
data:{
labels: ['0000-0100','0100-0200','0200-0300','0300-0400','0400-0500','0500-0600','0600-0700','0700-0800','0800-0900','0900-1000','1000-1100','1100-1200','1200-1300','1300-1400','1400-1500','1500-1600','1600-1700','1700-1800','1800-1900','1900-2000'],
datasets:[
{
label: 'speed',
type: 'line',
data:[69,67,64,70,71,66,67,69,67,68,67],
borderColor: 'red',
},
{
label: 'car_number',
type: 'line',
data:[
// i wnat to only show x:0,x:2,x:4,x:8,x:15 forever
{x:0, y:351, status: "觀測上游達門檻值,丹路路段車多,啟動疏運措施"},
{x:1, y:451, status:"Null"},
{x:2, y:751, status:"新路、丹路外環路段實施交違漸變延長措施"},
{x:3, y:851, status:"Null"},
{x:4, y:461, status:"交維措施撤離"},
{x:5, y:731, status:"Null"},
{x:6, y:131, status:"Null"},
{x:7, y:451, status:"Null"},
{x:8, y:331, status:"北上車流趨緩,恢復各路口平時制"},
{x:9, y:911, status:"Null"},
{x:10, y:341, status:"Null"},
{x:11, y:641, status:"Null"},
{x:12, y:531, status:"Null"},
{x:13, y:761, status:"Null"},
{x:14, y:451, status:"Null"},
{x:15, y:121, status:"北上車流趨緩,恢復各路口平時制"},
{x:16, y:321, status:"Null"},
{x:17, y:391, status:"Null"},
{x:18, y:561, status:"Null"},
{x:19, y:321, status:"Null"}
]
}
]
},
options: {
plugins: {
title: {
display: true,
text: '車流量及速率關係圖',
padding: {
top: 10,
},
font: {
size: 18
}
},
tooltip:{
backgroundColor: 'rgb(255, 255, 56)',
bodyColor:'#000000',
bodyFont:{
size:15
},
callbacks:{
label:(context) =>{
return `${context.label}, status: ${context.raw.status}`;
}
}
}
},
}
})
</script>

Related

The chart.js zoom and pan plugin is not working in WKWebView

I added the zoom and pan functionality to a graph.
This works in Chrome and Safari. But when embedded in a SwiftUI WKWebview the entire graph zooms as if it is an image. I do not get the proper axis scaling.
I basically followed this tutorial to add the zoom. https://www.youtube.com/watch?v=QUzVVPK1Nks&ab_channel=ChartJS
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js" integrity="sha512-UXumZrZNiOwnTcZSHLOfcTs0aos2MzBWHXOHOuB0J/R44QB0dwY5JgfbvljXcklVf65Gc4El6RjZ+lnwd2az2g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/1.2.1/chartjs-plugin-zoom.min.js" integrity="sha512-klQv6lz2YR+MecyFYMFRuU2eAl8IPRo6zHnsc9n142TJuJHS8CG0ix4Oq9na9ceeg1u5EkBfZsFcV3U7J51iew==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<div id="container" style="position: relative; height:95vh; width:95vw">
<canvas id="myCanvas"></canvas>
</div>
<script>
//var incomingData = __#data__;
var ctx = document.getElementById("myCanvas").getContext('2d');
var gradient = ctx.createLinearGradient(0, 0, 0, 500);
gradient.addColorStop(0, 'rgba(158,200,62,1)');
gradient.addColorStop(1, 'rgba(188,190,50,1)');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["5:30 PM","5:45 PM","6:00 PM","6:15 PM","6:30 PM","6:45 PM","7:00 PM","7:15 PM","7:30 PM","7:45 PM","8:00 PM","8:15 PM","8:30 PM","8:45 PM","9:00 PM","9:15 PM","9:30 PM","9:45 PM","10:00 PM","10:15 PM","10:30 PM","10:45 PM","11:00 PM","11:15 PM","11:30 PM","11:45 PM","12:00 AM","12:00 AM","12:15 AM","12:30 AM","12:45 AM","1:00 AM","1:15 AM","1:30 AM","1:45 AM","2:00 AM","2:15 AM","2:30 AM","2:45 AM","3:00 AM","3:15 AM","3:30 AM","3:45 AM","4:00 AM","4:15 AM","4:30 AM","4:45 AM","5:00 AM","5:15 AM","5:30 AM","5:45 AM","6:00 AM","6:15 AM","6:30 AM","6:45 AM","7:00 AM","7:15 AM","7:30 AM","7:45 AM","8:00 AM","8:15 AM","8:30 AM","8:45 AM","9:00 AM","9:15 AM","9:30 AM","9:45 AM","10:00 AM","10:15 AM","10:30 AM","10:45 AM","11:00 AM","11:15 AM","11:30 AM","11:45 AM","12:00 PM","12:15 PM","12:30 PM","12:45 PM","1:00 PM","1:15 PM","1:30 PM","1:45 PM","2:00 PM","2:15 PM","2:30 PM","2:45 PM","3:00 PM","3:15 PM","3:30 PM","3:45 PM","4:00 PM","4:15 PM","4:30 PM","4:45 PM","5:00 PM"],
datasets: [{
backgroundColor : gradient, // Put the gradient here as a fill color
label: 'Temp',
data: [21.62,21.54,21.66,21.39,23,22.31,21.18,21.12,28.18,22.1633333333333333,20.66,20.18,19.7266666666666667,19.35,32.04,30.9533333333333333,33.18,31.9733333333333333,43.035,32.6225,31.7033333333333333,48.32,33.58,33.64,22.37,20.9,20.56,20.56,20.43,20.39,20.25,20,19.87,19.68,19.62,19.5,19.355,19.18,19.06,19,18.87,18.79,18.68,18.68,18.62,18.56,18.5,18.385,18.25,18.12,18,17.91,17.81,17.75,17.62,17.5,17.5,17.5,17.5,18.43,18.68,18.85,19.12,19.25,19.31,19.37,19.43,NaN,NaN,NaN,NaN,19.6,19.68,19.81,19.87,19.9533333333333333,20,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,0,NaN,NaN,NaN,NaN,NaN,NaN,NaN,22.18,22.37,22.31],
borderColor: '#9ec83e',
borderWidth: '1',
fill: true,
}]
},
options: {
plugins: {
legend:{
display: false,
},
zoom: {
limits: {
x: {min: 0, max: 400, minRange: 50},
y: {min: 0, max: 200, minRange: 50}
},
pan: {
enabled: true,
mode: 'xy',
},
zoom: {
wheel: {
enabled: true,
},
pinch: {
enabled: true
},
mode: 'xy',
}
}
},
maintainAspectRatio: false,
animation: {
duration: 0, // general animation time
},
elements: {
point: {
radius: 0
}
},
scales: {
x: {
grid: {
display:false
}
},
y: {
grid: {
display:false
},
ticks: {
autoSkip: true,
maxTicksLimit: 4
}
}
},
}
});
Chart.register(zoomPlugin);
</script>
</body>
</html>
I have tried the following in Swift
wkWebview.scrollView.delegate = self
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return nil
}

Horizontal floating bars with a time y-axis in Chart.js

I'm trying to adapt https://www.chartjs.org/docs/latest/samples/bar/floating.html to create a kind of timeline chart, where the horizontal axis (the range data) are timestamps. Unfortunately I can't seem to get the data to display, and don't see any error messages, etc.
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/moment#^2"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js#^3"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment#^1"></script>
</head>
<body>
<div>
<canvas id="myChart"></canvas>
</div>
</body>
<script>
//const labels = ["a","b","c","d","fff","g","h"]
const labels = ["a","b"]
const data = {
labels: labels,
datasets: [
{
label: 'Dataset 1',
data: [[moment("2021-11-23T00:24:14Z"),moment("2021-11-23T00:34:03Z")],
[moment("2021-11-23T00:24:14Z"),moment("2021-11-23T00:26:35Z")]]
// This works fine (without 'yAxes' below):
//data: labels.map(() => {
//return [Math.random(), Math.random()];
//}),
//backgroundColor: Utils.CHART_COLORS.red,
},
]
};
const config = {
type: 'bar',
data: data,
options: {
yAxes: [{
type: 'time',
}],
indexAxis: 'y',
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Chart.js Floating Bar Chart'
}
}
}
};
var ctx = document.getElementById("myChart").getContext("2d");
new Chart(ctx, config);
</script>
</html>
EDIT: JSFiddle: https://jsfiddle.net/r5ypuvks/
Mmm it seems my issues are:
not using proper axis format for chart.js 3
need to scale the graph myself
need to refer to horizontal axis as x
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/moment#^2"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js#^3"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment#^1"></script>
</head>
<body>
<div>
<canvas id="myChart"></canvas>
</div>
</body>
<script>
//const labels = ["a","b","c","d","fff","g","h"]
const labels = ["a","b"]
const data = {
labels: labels,
datasets: [
{
label: 'Dataset 1',
data: [[("2021-11-23T00:24:14Z"),("2021-11-23T00:34:03Z")],
[("2021-11-23T00:25:14Z"),("2021-11-23T00:26:35Z")]]
// This works fine (without 'yAxes' below):
//data: labels.map(() => {
//return [Math.random(), Math.random()];
//}),
//backgroundColor: Utils.CHART_COLORS.red,
},
]
};
const config = {
type: 'bar',
data: data,
options: {
scales: {
x: {
min: ("2021-11-23T00:20:14Z"),
max: ("2021-11-23T00:44:14Z") ,
type: 'time',
},
},
indexAxis: 'y',
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Chart.js Floating Bar Chart'
}
}
}
};
var ctx = document.getElementById("myChart").getContext("2d");
new Chart(ctx, config);
</script>
</html>

Chart.js v3.x time series on x axis

so I'm basically pulling my hair as I can't get this to work for hours straight.
I'm trying to do a (I assumed simple) line-graph with on the x-axis time of day in hours and on the y-axis number of views. I'm trying to set the x-axis range as -24 hours until now.
My code is as follows. What am I doing wrong?
<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/2.7.3/Chart.bundle.min.js"></script>
<div style="width: 500px; height: 500px;"><canvas id="myChart" width="400" height="400"></canvas></div>
<script>
var ctx = document.getElementById('myChart');
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: '# of Votes',
data: [{x:'1619701200',y:41},{x:'1619704800',y:9},{x:'1619708400',y:21}]
}]
},
options: {
scales: {
x: {
type: 'time',
min: Date.now() - (24 * 60 * 60 * 1000),
max: Date.now()
}
}
}
});
</script>
EDIT: the problem is that the x-axis doesn't extend to 24 hours prior to now(). Also, there are 3 values in the dataset, but only two are shown. You can even edit the x-values to whatever you want and the entire graph stays the same.
EDIT2:
Could someone help me get this right? I've pasted my data below:
What I am trying to achieve:
X-axis going from now until 24 hours prior with an interval of 1 hour between ticks, formatted as 'd-m-Y H:00:00'. The data now is in seconds since epoch, if I need to change that please let me know!
Y-axis going from 0 to whatever the max is in the dataset
What CDNs do I have to include? I find the documentation on chart.js, moments, adapters etc quite unclear and everything I find on the internet is for prior versions.
Thank you!!
<div style="width: 500px; height: 500px;"><canvas id="myChart" width="400" height="400"></canvas></div>
<script>
new Chart(document.getElementById("myChart"), {
type: 'line',
data: {
labels: ['1619701200','1619704800','1619708400','1619715600','1619719200','1619722800','1619726400','1619730000','1619733600','1619737200','1619744400','1619773200','1619780400','1619784000','1619787600','1619791200','1619794800','1619798400','1619802000','1619809200','1619812800','1619816400','1619820000','1619823600','1619856000'],
datasets: [{
data: [41,9,21,80,277,151,68,88,82,48,12,1,97,36,81,21,63,49,44,15,10,44,81,4,9],
label: "Views",
borderColor: "#3e95cd",
fill: false
},
{
data: [1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,4,1,1],
label: "Visitors",
borderColor: "#3e95cd",
fill: false
}
]
}
</script>
We want you to keep your hair :-)
Try the following 2 Options for latest version of Chart.js
Chart.js v3.2.1 (not backwards compatible with v2.xx)
Option 1:
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
// gets you the latest version of Chart.js, now at v3.2.1
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment"></script>
// You need moment.js and adapter for time or timeseries to work at x-Axis
<div style="width: 500px; height: 500px">
<canvas id="myChart" width="400" height="400"></canvas>
</div>
<script>
const startDate = new Date(1619701200*1000);
// first label from your data, times 1000 to get milliseconds,
// for last 24 hours from now, see further down below
const myLabels = [];
let nextHour = startDate;
let i = 0; // tip: declare i outside for-loop for better performance
for (i; i < 24; i++) {
nextHour = new Date((1619701200 + (i*3600)) *1000);
myLabels.push(nextHour);
};
const ctx = document.querySelector('canvas').getContext('2d');
const myChart3x = new Chart(ctx, {
type: 'line',
data: {
labels: myLabels,
datasets: [{
data: [41,9,21,80,277,151,68,88,82,48,12,1,97,36,81,21,63,49,44,15,10,44,81,4,9],
label: "Views",
borderColor: "#3e95cd",
fill: false
},
{
data: [1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,4,1,1],
label: "Visitors",
borderColor: "#3e95cd",
fill: false
}
]
},
options: {
scales: {
x: {
type: 'timeseries',
time: {
unit: 'hour', // <-- that does the trick here
displayFormats: {
hour: 'D-M-Y H:00:00'
},
tooltipFormat: 'D-M-Y H:00:00' // <-- same format for tooltip
}
},
y: {
min: 0
}
}
}
});
</script>
And this is what your chart would look like:
If you want to calculate dynamically the last 24 hours from now for your x-Axis, I would suggest to use moment.js instead:
<script>
// ...
const startDate = moment().subtract(1, 'd');
const myLabels = [];
let nextHour = startDate;
let i = 0;
for (i; i < 24; i++) {
nextHour = moment().add(i, 'h');
myLabels.push(nextHour);
};
// ....
</script>
Also, be aware that moment.js uses slightly different formatting string:
'D-M-Y H:00:00' instead of 'd-m-Y H:00:00'
Option 2:
If you have your data in json-format
data: [{x:1620237600000,y:41},{x:1620241200000,y:9},{x:1620244800000,y:21}]
like your first code snippet on top, using min and max at x-Axis: (Advantage: you don't have to define labels-array for x-Axis)
<script>
const ctx = document.querySelector("canvas").getContext("2d");
var myChart = new Chart(ctx, {
type: "line",
data: {
datasets: [{
label: "Views",
data: [{x:1620237600000,y:41},{x:1620241200000,y:9},{x:1620244800000,y:21}]
// x-value without quotes (has to be a number)
// and multiply by 1000 to get milliseconds
},
{
label: "Visitors",
data: [{x:1620237600000,y:1},{x:1620241200000,y:1},{x:1620244800000,y:2}]
}]
},
options: {
scales: {
x: {
type: "time", // <-- "time" instead of "timeseries"
min: Date.now() - (24 * 60 * 60 * 1000),
max: Date.now(),
time: {
unit: "hour", // <-- that does the trick here
displayFormats: {
hour: "D-M-Y H:00:00"
},
tooltipFormat: "D-M-Y H:00:00"// <-- same format for tooltip
}
},
y: {
min: 0,
max: 100
}
}
}
});
</script>
You should get the following:
I hope I understood correctly your need and hope this helps.
It needs more settings, I've searched and by trial/error - credit to this jsfiddle - , these are the results.
See updated working jsfiddle:
/*
Source: https://jsfiddle.net/microMerlin/3wfoL7jc/
*/
var ctx = document.getElementById('myChart');
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: '# of Votes',
data: [{
x: '1619701200',
y: 41
}, {
x: '1619704800',
y: 9
}, {
x: '1619708400',
y: 21
}]
}]
},
options: {
responsive: true,
scales: {
xAxes: [{
min: Date.now() - (24 * 60 * 60 * 1000),
max: Date.now(),
type: "linear",
position: "bottom",
//stacked: true,
ticks: {
//beginAtZero: true,
userCallback: function(t, i) {
/*console.log("t: " + t.toString());
console.log("i: " + i.toString());*/
return i;
}
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div style="width: 500px; height: 500px;"><canvas id="myChart" width="400" height="400"></canvas></div>

Chartjs 2: Multi level/hierarchical category axis in chartjs

Is it possible to define a bar chart with a multi level/category axis?
For instance I'd like to display the Region/Province categories like in this Excel chart:
I found this example using multiple xAxes.
xAxes:[
{
id:'xAxis1',
type:"category",
ticks:{
callback:function(label){
var month = label.split(";")[0];
var year = label.split(";")[1];
return month;
}
}
},
{
id:'xAxis2',
type:"category",
gridLines: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
ticks:{
callback:function(label){
var month = label.split(";")[0];
var year = label.split(";")[1];
if(month === "February"){
return year;
}else{
return "";
}
}
}
}
]
The problem is it seems that the two axes are not really linked and the alignment of second axis is based on values instead of aligning in middle of lower level category. this case cause issues
Is there a clean way to achieve this in chart.js?
Update:
I ended up creating a feature request on chartjs.
you can provide value separately for different axis via datesets and provide an object with different configuration option (borderColor, pointBackgroundColor, pointBorderColor) etc, i hope It'll help.
here is the link for the with an update (fiddle you shared) Updated Fiddle
data: {
labels: ["January;2015", "February;2015", "March;2015", "January;2016", "February;2016", "March;2016"],
datasets: [{
label: '# of Votes',
xAxisID:'xAxis1',
data: [12, 19, 3, 5, 2, 3]
},
// here i added another data sets for xAxisID 2 and it works just fine
{
label: '# of Potatoes',
xAxisID:'xAxis2',
data: [9, 17, 28, 26, 29, 9]
}]
}
I hope that solves your problem :)
Hope this helps,
I did a bit of research and couldn't find methods to implement your solution in chartjs. Chartjs has grouped bar charts but not subgrouped bar charts like in your case.
Example: http://jsfiddle.net/harshakj89/ax3zxtzw/
Here are some alternatives,
D3js (https://d3js.org/) can be used to create sub grouped bar charts.Data can be loaded from csv or json. D3 is highly configurable, but you may have to put some effort than chartsjs.
https://plnkr.co/edit/qGZ1YuyFZnVtp04bqZki?p=preview
https://stackoverflow.com/questions/37690018/d3-nested-grouped-bar-chart
https://stackoverflow.com/questions/15764698/loading-d3-js-data-from-a-simple-json-string
ZingChart is a commercial tool and can be used to implement bar charts with sub groupes.
https://www.zingchart.com/docs/chart-types/bar-charts/
But I prefer D3 over this library. because D3 comes under BSD License.
This should work as per your requirement http://tobiasahlin.com/blog/chartjs-charts-to-get-you-started/#8-grouped-bar-chart
The best library I could found to have exactly this feature is Highcharts, this is my implementation:
and here http://jsfiddle.net/fieldsure/Lr5sjh5x/2/ you can find out how to implement it.
$(function () {
var chart = new Highcharts.Chart({
chart: {
renderTo: "container",
type: "column",
borderWidth: 5,
borderColor: '#e8eaeb',
borderRadius: 0,
backgroundColor: '#f7f7f7'
},
title: {
style: {
'fontSize': '1em'
},
useHTML: true,
x: -27,
y: 8,
text: '<span class="chart-title"> Grouped Categories with 2 Series<span class="chart-href"> Black Label </span> <span class="chart-subtitle">plugin by </span></span>'
},
yAxis: [{ // Primary yAxis
labels: {
format: '${value}',
style: {
color: Highcharts.getOptions().colors[0]
}
},
title: {
text: 'Daily Tickets',
style: {
color: Highcharts.getOptions().colors[0]
}
}
}, { // Secondary yAxis
title: {
text: 'Invoices',
style: {
color: Highcharts.getOptions().colors[0]
}
},
labels: {
format: '${value}',
style: {
color: Highcharts.getOptions().colors[0]
}
},
opposite: true
}]
,
series: [{
name: 'Daily',
type: 'column',
yAxis: 1,
data: [4, 14, 18, 5, 6, 5, 14, 15, 18],
tooltip: {
valueSuffix: ' mm'
}
}, {
name: 'Invoices',
type: 'column',
data: [4, 17, 18, 8, 9, 5, 13, 15, 18],
tooltip: {
valueSuffix: ' °C'
}
}],
xAxis: {
categories: [{
name: "1/1/2014",
categories: ["Vendor 1", "Vendor 2", "Vendor 3"]
}, {
name: "1/2/2014",
categories: ["Vendor 1", "Vendor 2", "Vendor 3"]
}, {
name: "1/3/2014",
categories: ["Vendor 1", "Vendor 2", "Vendor 3"]
}]
}
});
});
body {
padding: 0px !important;
margin: 8px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://blacklabel.github.io/grouped_categories/grouped-categories.js"></script>
<div id="container" class="chart-container"></div>
But the problem is the library is not free for commercial purposes, and this is the Chartjs implementation, in my case it is look like this:
const data ={"labels":[{"label":"Exams","children":["Wellness Examination"]},{"label":"Surgery","children":["Neuter Surgery"]},{"label":"Vaccines","children":["Bordetella"]},{"label":"Dentistry","children":["Dental Cleaning"]},{"label":"Diagnostics","children":["Other","Pre-Anesthetic","Adult Diagnostics","Pre-Anesthetic Diagnostics","Heartworm & Tick Borne Disease Test"]},{"label":"Treatments/Other","children":["Other","Microchip"]}],"datasets":[{"label":"Consumed","backgroundColor":"red","tree":[{"value":0,"children":["0"]},{"value":0,"children":["0"]},{"value":1,"children":["1"]},{"value":0,"children":["0"]},{"value":15,"children":["0","1","3","11","0"]},{"value":15,"children":["2","13"]}]},{"label":"Purchased","backgroundColor":"blue","tree":[{"value":28,"children":["28"]},{"value":1,"children":["1"]},{"value":24,"children":["24"]},{"value":10,"children":["10"]},{"value":103,"children":["2","16","34","49","2"]},{"value":165,"children":["75","90"]}]}]};
window.onload = () => {
const ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx, {
type: 'bar',
data: data,
options: {
responsive: true,
title: {
display: true,
text: 'Chart.js Hierarchical Bar Chart'
},
layout: {
padding: {
// add more space at the bottom for the hierarchy
bottom: 45
}
},
scales: {
xAxes: [{
type: 'hierarchical',
stacked: false,
// offset settings, for centering the categorical
//axis in the bar chart case
offset: true,
// grid line settings
gridLines: {
offsetGridLines: true
}
}],
yAxes: [{
stacked: false,
ticks: {
beginAtZero: true
}
}]
}
}
});
};
canvas {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/chart.js/dist/Chart.bundle.js"></script>
<script src="https://unpkg.com/chartjs-scale-hierarchical"></script>
<div id="container" style="width: 75%;">
<canvas id="canvas"></canvas>
</div>
for each more column just add another dataset.

custom tooltips with react-chartjs-2 library

I am having issue with the default tooltip that chartjs provides as I can not add html inside the tooltips. I had been looking at how i can add the html/jsx inside the tooltip. I see an example with using customized tooltips here Chart JS Show HTML in Tooltip.
can someone point me an example how to achieve the same with react-chartjs-2 library?
You have to use the custom callback in the tooltip property to define your own positioning and set the hovered dataset in the component state
state = {
top: 0,
left: 0,
date: '',
value: 0,
};
_chartRef = React.createRef();
setPositionAndData = (top, left, date, value) => {
this.setState({top, left, date, value});
};
render() {
chartOptions = {
"tooltips": {
"enabled": false,
"mode": "x",
"intersect": false,
"custom": (tooltipModel) => {
// if chart is not defined, return early
chart = this._chartRef.current;
if (!chart) {
return;
}
// hide the tooltip when chartjs determines you've hovered out
if (tooltipModel.opacity === 0) {
this.hide();
return;
}
const position = chart.chartInstance.canvas.getBoundingClientRect();
// assuming your tooltip is `position: fixed`
// set position of tooltip
const left = position.left + tooltipModel.caretX;
const top = position.top + tooltipModel.caretY;
// set values for display of data in the tooltip
const date = tooltipModel.dataPoints[0].xLabel;
const value = tooltipModel.dataPoints[0].yLabel;
this.setPositionAndData({top, left, date, value});
},
}
}
return (
<div>
<Line data={data} options={chartOptions} ref={this._chartRef} />
{ this.state.showTooltip
? <Tooltip style={{top: this.state.top, left: this.state.left}}>
<div>Date: {this.state.date}</div>
<div>Value: {this.state.value}</div>
</Tooltip>
: null
}
</div>
);
}
You can use the tooltips supplied by React Popper Tooltip or roll your own - pass the top and left to the tooltip for positioning, and the date and value (in my example) should be used to show the data in the tooltip.
If anyone looking answer customization of tooltip and gradient chart here is my code:
My Packages:
"react": "^17.0.2"
"chart.js": "^3.7.1"
"react-chartjs-2": "^4.1.0"
"tailwindcss": "^3.0.23"
ToopTip Component:
import React, { memo } from "react";
import { monetarySuffix } from "#src/helpers/util";
// tooltip.js
const GraphTooltip = ({ data, position, visibility }) => {
return (
<div
className={`absolute px-4 py-3.5 rounded-lg shadow-lg bg-chart-label-gradient text-white overflow-hidden transition-all duration-300 hover:!visible
${visibility ? "visible" : "invisible"}
`}
style={{
top: position?.top,
left: position?.left,
}}
>
{data && (
<>
<h5 className="w-full mb-1.5 block text-[12px] uppercase">
{data.title}
</h5>
<ul className="divide-y divide-gray-100/60">
{data.dataPoints.map((val, index) => {
return (
<li
key={index}
className="m-0 py-1.5 text-base font-rubik font-medium text-left capitalize last:pb-0"
>
{val?.dataset.label}
{":"} {monetarySuffix(val?.raw)}
</li>
);
})}
</ul>
</>
)}
</div>
);
};
export default memo(GraphTooltip);
Chart Component
import React, { useMemo, useState, useRef, useCallback } from 'react';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
Filler,
} from 'chart.js';
import { Line } from 'react-chartjs-2';
import GraphTooltip from './chart-tooltip';
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
Filler
);
const GradientChart = () => {
const [tooltipVisible, setTooltipVisible] = useState(false);
const [tooltipData, setTooltipData] = useState(null);
const [tooltipPos, setTooltipPos] = useState(null);
const chartRef = useRef(null);
const data = {
labels: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'Agust',
'September',
'October',
'November',
'December',
],
datasets: [
{
fill: true,
backgroundColor: (context) => {
const chart = context.chart;
const { ctx, chartArea } = chart;
if (!chartArea) {
return;
}
return createGradient(
ctx,
chartArea,
'#F46079',
'#F46079',
'rgba(255,255,255,0)'
);
},
borderColor: '#F46079',
lineTension: 0.4,
pointRadius: 5,
pointHoverRadius: 10,
pointBackgroundColor: '#FE5670',
pointBorderColor: '#ffffff',
pointBorderWidth: 1.5,
label: 'Sales',
data: [
4500, 2800, 4400, 2800, 3000, 2500, 3500, 2800, 3000, 4000, 2600,
3000,
],
},
{
fill: true,
backgroundColor: (context) => {
const chart = context.chart;
const { ctx, chartArea } = chart;
if (!chartArea) {
return;
}
return createGradient(
ctx,
chartArea,
'#2f4b7c',
'#2f4b7c',
'rgba(255,255,255,0)'
);
},
borderColor: '#2f4b7c',
lineTension: 0.4,
pointRadius: 5,
pointHoverRadius: 10,
pointBackgroundColor: '#FE5670',
pointBorderColor: '#ffffff',
pointBorderWidth: 1.5,
label: 'Commision',
data: [
5000, 3500, 3000, 5500, 5000, 3500, 6000, 1500, 2000, 1800, 1500,
2800,
],
},
{
fill: true,
backgroundColor: (context) => {
const chart = context.chart;
const { ctx, chartArea } = chart;
if (!chartArea) {
return;
}
return createGradient(
ctx,
chartArea,
'#665191',
'#665191',
'rgba(255,255,255,0)'
);
},
borderColor: '#665191',
lineTension: 0.4,
pointRadius: 5,
pointHoverRadius: 10,
pointBackgroundColor: '#FE5670',
pointBorderColor: '#ffffff',
pointBorderWidth: 1.5,
label: 'Transaction',
data: [
1000, 2000, 1500, 2000, 1800, 1500, 2800, 2800, 3000, 2500, 3500,
2800,
],
},
],
};
const createGradient = (ctx, chartArea, c1, c2, c3) => {
const chartWidth = chartArea.right - chartArea.left;
const chartHeight = chartArea.bottom - chartArea.top;
const gradient = '';
const width = '';
const height = '';
if (!gradient || width !== chartWidth || height !== chartHeight) {
width = chartWidth;
height = chartHeight;
gradient = ctx.createLinearGradient(
0,
chartArea.bottom,
0,
chartArea.top
);
gradient.addColorStop(0, c3);
gradient.addColorStop(0.5, c2);
gradient.addColorStop(1, c1);
}
return gradient;
};
const customTooltip = useCallback((context) => {
if (context.tooltip.opacity == 0) {
// hide tooltip visibilty
setTooltipVisible(false);
return;
}
const chart = chartRef.current;
const canvas = chart.canvas;
if (canvas) {
// enable tooltip visibilty
setTooltipVisible(true);
// set position of tooltip
const left = context.tooltip.x;
const top = context.tooltip.y;
// handle tooltip multiple rerender
if (tooltipPos?.top != top) {
setTooltipPos({ top: top, left: left });
setTooltipData(context.tooltip);
}
}
});
const options = useMemo(() => ({
responsive: true,
scales: {
y: {
grid: {
display: false,
},
},
},
interaction: {
mode: 'index',
intersect: false,
},
plugins: {
legend: {
display: false,
},
title: {
display: false,
},
tooltip: {
enabled: false,
position: 'nearest',
external: customTooltip,
},
},
}));
return (
<div className="grad-chart-wrapper w-full relative">
<Line options={{ ...options }} data={data} ref={chartRef} />
{tooltipPos && (
<GraphTooltip
data={tooltipData}
position={tooltipPos}
visibility={tooltipVisible}
/>
)}
</div>
);
};
export default GradientChart;
Remember to think in React here (which is not always easy). Use the mycustomtooltipfunction to set state in your React class (specifically, add the tooltip that is passed to mycustometooltipfunction to the state - this will result in render being invoked. Now in the render function of your class, check if that state exists and add the JSX for your tooltip.
class MyChart extends Component {
constructor(props) {
super(props);
this.state = {
tooltip : undefined
};
}
showTooltip = (tooltip) => {
if (tooltip.opacity === 0) {
this.setState({
tooltip : undefined
});
} else {
this.setState({
tooltip
});
}
}
render() {
const { tooltip } = this.state;
let options = {
...
tooltips : {
enabled : false,
custom : this.showTooltip,
}
}
let myTooltip;
if (tooltip) {
// MAKE YOUR TOOLTIP HERE - using the tooltip from this.state.tooltip, or even have a tooltip JSX class
}
return (
<div>
{myTooltip}
<Line ref="mygraph" key={graphKey} data={data} options={options} height={graphHeight} width={graphWidth}/>
</div>
)
}
}
`
this.chart.chart_instance.canvas.getBoundingClientRect();
If you get some error with chart_instance you should check parent of element value.
Try this:
this.chart.chartInstance.canvas.getBoundingClientRect();