Chart.JS value on top disappear when tooltip show up - javascript

I have chart witch i need show up value on top each bar, and i need show tooltip too, so i try these code
var ctx3 = document.getElementById('canvasppicinh').getContext('2d');
window.myMixedChart = new Chart(ctx3, {
type: 'bar',
data: chartData,
options: {
maintainAspectRatio: false,
title: {
display: true,
text: 'Chart KPI Financial Perfective',
padding: 50
},
legend :{
position : 'bottom'
},
tooltips: {
mode: 'index',
intersect: true,
enabled: true,
callbacks: {
label: function(tooltipItem, data) {
var val = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
return val.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") +',-';
}
}
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
categoryPercentage: 1.0
},
gridLines: {
display: false
}
}]
},
hover: {
mode: false
},
animation: {
duration : 1,
onProgress: function() {
var chartInstance = this.chart,
ctx = chartInstance.ctx;
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
this.data.datasets.forEach(function(dataset, i) {
var meta = chartInstance.controller.getDatasetMeta(i);
meta.data.forEach(function(bar, index) {
var data = dataset.data[index];
ctx.fillText(data, bar._model.x, bar._model.y - 5);
});
});
}
}
}
});
Well is kinda work for me, my chart show value on top each bar but my problem is, everytime i try show tooltip value on top disappear. If tooltip disappear (not hovering) top value appearing. Did i miss something? And I have searching these problem on google, it said i need extend. But i don't have any idea how to use extend with my oldest code. Please help me.

I found the solution you need plugin chartjs-plugin-datalabels, and add inside option chart
plugins: {
datalabels: {
color: 'white',
formatter: function(value, context) {
return Math.round(value) + '%';
}
}
}
And remove animation

Related

Add value to horizontal bar in chart.js

I have been looking around for a way to add the value inside each bar in a horizontal bar chart, using chart.js but all posts seem to show how to add the label. Tooltip shows both label and value but I wanted to show the value so users can see the result without having to mouse-over the bar.
I put break points in the script to see if there is any property of "model" that contains the value but couldn't find one.
This is how I set up the chart including the animation section that shows the label instead of the value.
Dataset comes from an ajax call and chart is displayed in its onSuccess:
function OnSuccess_(response) {
var jResult = JSON.parse(response.d);
var chartLabels = [];
var chartData = []
$.each(jResult, function (index, val) {
chartLabels.push(val.TIMERANGE);
chartData.push(val.COUNT); // <--- This is what I want to display on each bar
});
var ctx = document.getElementById("rtChart").getContext("2d");
if (chartRespTime)
chartRespTime.destroy();
chartRespTime = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: chartLabels,
datasets: [{
label: "Response Time (ms)",
backgroundColor: ["#3e95cd", "#8e5ea2", "#3cba9f", "#e8c3b9", "#c45850"],
data: chartData
}]
},
options: {
title: {
display: true,
text: 'Database Response Time (milli-seconds)'
},
scales: {
xAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Count'
},
ticks: {
major: {
fontStyle: 'bold',
fontColor: '#FF0000'
}
}
}],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Response Time (ms)'
}
}]
},
animation: {
onComplete: function () {
var ctx = this.chart.ctx;
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'left';
ctx.textBaseline = 'bottom';
this.data.datasets.forEach(function (dataset) {
for (var i = 0; i < dataset.data.length; i++) {
var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model,
left = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._xScale.left;
ctx.fillStyle = '#444'; // label color
var label = model.label; // <--- need value not label
ctx.fillText(label, left + 15, model.y + 8);
}
});
}
},
maintainAspectRatio: false,
responsive: true,
showInlineValues: true,
centeredInllineValues: true,
tooltipCaretSize: 0
}
});
//console.log(ctx);
$('#rtChart').show();
}
There is no build in way to do this, but there is a verry good plugin that achieves what you want: the datalabels plugin https://chartjs-plugin-datalabels.netlify.app/samples/charts/bar.html

How to add images to chart labels with vue-chartjs?

I want to add flag icons under the country code labels but am completely stuck.
Image of the chart with my current code
The images are named BR.svg, FR.svg and MX.svg and are located under #/assets/icons/flags/
I am using vue#2.6.12 and vue-chartjs#3.5.1 in my project. This is my Chart.vue component:
<script>
import { Bar } from 'vue-chartjs'
export default {
extends: Bar,
data: () => ({
chartdata: {
labels: ['BR', 'FR', 'MX'],
datasets: [
{
label: 'Lorem ipsum',
backgroundColor: '#AF78D2',
data: [39, 30, 30],
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
legend: {
display: false,
},
tooltips: {
"enabled": false
},
scales : {
xAxes : [ {
gridLines : {
display : false
}
} ],
yAxes: [{
ticks: {
beginAtZero: true,
suggestedMin: 0,
suggestedMax: 40,
stepSize: 5,
}
}]
},
"hover": {
"animationDuration": 0
},
"animation": {
"duration": 1,
"onComplete": function() {
var chartInstance = this.chart,
ctx = chartInstance.ctx;
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
this.data.datasets.forEach(function(dataset, i) {
var meta = chartInstance.controller.getDatasetMeta(i);
meta.data.forEach(function(bar, index) {
var data = dataset.data[index] + '%';
ctx.fillText(data, bar._model.x, bar._model.y - 5);
});
});
}
},
}
}),
mounted () {
this.renderChart(this.chartdata, this.options)
}
}
</script>
This runnable code below is the closest to a solution I have come by hours of searching. But it still won't do the trick because I don't know how to integrate it with what I have.
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'];
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);
var image = new Image();
image.src = images[index],
ctx.drawImage(image, 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>
When I have added the plugin code into my own code I get an error message saying 'plugins' is already defined in props, but I can't manage to use it somehow.
Anyone who knows how to implement this afterDraw plugin into my code? I appreciate any input.
Thanks a lot in advance! :)
In the mounted of your vue component you can call the addPlugin (has to be done before the renderChart method) like this:
this.addPlugin({
id: 'image-label',
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);
var image = new Image();
image.src = images[index],
ctx.drawImage(image, x - 12, yAxis.bottom + 10);
});
}
})
Documentation: https://vue-chartjs.org/api/#addplugin
It works in ChartJS version 4.0.1. data needs to return 'plugins':
data() {
return {
plugins: [{
afterDraw: chart => {
const ctx = chart.ctx;
const xAxis = chart.scales['x'];
const yAxis = chart.scales['y'];
xAxis.ticks.forEach((value, index) => {
let x = xAxis.getPixelForTick(index);
ctx.drawImage(images[index], x - 12, yAxis.bottom + 10)
});
}
}],
data: {...
Please note that ctx should be chart.ctx and NOT chart.chart.ctx.. Similarly, it should be chart.scales['x'] and NOT chart.scales['x-axis-0'].
After you return plugins, this needs to be referenced in your Bar component like so..
<Bar :data="data" :options="options" :plugins="plugins"/>

Want to show small part of js Bar Chart when value is zero

I have these bar charts that will sometimes show zero values to the user. I'd like to show a bit of the bar chart so it does not look completely empty, but the data is connected to the visual representation of the bar. Is there any way I could have the top number say zero but the value be 3(or something small) in each column? Here is the code and a screenshots. One screenshot has data, the other is zero. I'd like the zero graph to show just a bit of the orange and green when at zero. Thanks
<canvas id="bar-chart" width="900" height="350"></canvas>
<script type="text/javascript">
var ctx = document.getElementById("bar-chart");
debugger;
var data = {
labels: ["Page Views", "Data Requests"],
datasets: [{
data: [<?php echo $databaseClass->totalViewsSelectedMonth($user_id, $year, 1);
?>, <?php
echo $databaseClass->isRfYearMonth($user_id, $year, 1);
?>],
backgroundColor: ["orange", "green"]
}]
}
var myChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
"hover": {
"animationDuration": 0
},
"animation": {
"duration": 1,
"onComplete": function() {
var chartInstance = this.chart,
ctx = chartInstance.ctx;
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
this.data.datasets.forEach(function(dataset, i) {
var meta = chartInstance.controller.getDatasetMeta(i);
meta.data.forEach(function(bar, index) {
var data = dataset.data[index];
ctx.fillText(data, bar._model.x, bar._model.y - 5);
});
});
}
},
legend: {
"display": false
},
tooltips: {
"enabled": false
},
scales: {
yAxes: [{
display: false,
gridLines: {
display: false
},
ticks: {
max: Math.max(...data.datasets[0].data) + 20,
display: false,
beginAtZero: true
}
}],
xAxes: [{
gridLines: {
display: false
},
ticks: {
beginAtZero: true
}
}]
}
}
});
</script>
It looks like you can update the axes of your charts to a new default. So, using a modified form of their example, something like the following (modified to fit your code):
// ...
scales: {
yAxes: [{
display: false,
gridLines: {
display: false
},
ticks: {
max: Math.max(...data.datasets[0].data) + 20,
min: -3, // ADDED
display: false,
beginAtZero: true
}
}],
// ...
... would do what you're looking for - with the side benefit that 0 and 3 wouldn't look like the same value, the relative bar size should stay accurate.

Angular chart.js onClick is not working - how to get all elements of a bar when it is clicked on

I am a beginner to Chart.js and I am trying to create click events when a chart is created.
I am trying to access all the elements of a bar when it is clicked on, but right now, the onClick method is not even running properly. The chart is rendering just fine - any html should not be needed as the canvas is created in the code. I would appreciate help in order to solve this problem
controller($state, $scope, $rootScope) {
$scope.myChart;
....
$scope.go = function myFunc() {
document.getElementById("chartContainer").innerHTML = ' ';
document.getElementById("chartContainer").innerHTML = '<canvas style="margin-top: 10px; padding-top: 20px; height:90% ; background-color: ' + vm.options.backgroundColor + '; " id="myChart" click="onClick"></canvas>';
var ctx = document.getElementById("myChart").getContext('2d');
render($scope.myChart, ctx, vm.options.barColor, vm.options.backgroundColor, labelVal, value);
};
$scope.onClick = function (evt) {
console.log("Testing");
};
}
var render = function createChart(myChart, ctx, barColor, backgroundColor, labels, values) {
myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
backgroundColor: barColor,
data: values,
}]
},
options: {
events: ['click'],
chartArea: {
backgroundColor: backgroundColor,
},
global: {
responsive: true,
maintainAspectRatio: false,
},
scaleBeginAtZero: true,
maintainAspectRatio: false,
scales: {
xAxes: [{
ticks: {
autoSkip: false,
}
}],
yAxes: [{
ticks: {
beginAtZero: true,
min: 0
}
}]
}
}
});
}
TRY adding the following on-click event handler in your chart options ...
options: {
onClick: function(e) {
var element = this.getElementAtEvent(e);
if (element.length) {
console.log(element[0])
}
},
...
}
This should work as far as I can tell

Hide Y-axis labels when data is not displayed in Chart.js

I have a Chart.js bar graph displaying two sets of data: Total SQL Queries and Slow SQL Queries. I have Y-axis labels for each respective set of data. The graph can be seen below:
When I toggle one of the sets of data to not display, the corresponding Y-axis labels still display. When interpreting the graph, this is a bit confusing. As seen below:
My question: How can I hide the Y-axis labels of any set of data that is currently not being displayed?
This is how I currently have my chart set up:
<canvas id="SQLPerformanceChart" minHeight="400"></canvas>
<script type="text/javascript">
...
var data = {
labels: labelArray,
datasets: [{
label: "Total SQL Queries",
fill: false,
borderWidth: 1,
borderColor: "green",
backgroundColor: "rgba(0, 255, 0, 0.3)",
yAxisID: "y-axis-0",
data: totalQueriesArray
}, {
label: "Slow SQL Queries",
fill: false,
borderWidth: 1,
borderColor: "orange",
backgroundColor: "rgba(255, 255, 0, 0.3)",
yAxisID: "y-axis-1",
data: slowQueriesArray,
}]
};
var options = {
animation: false,
scales: {
yAxes: [{
position: "left",
ticks: {
beginAtZero: true
},
scaleLabel: {
display: true,
labelString: 'Total SQL Queries'
},
id: "y-axis-0"
}, {
position: "right",
ticks: {
beginAtZero: true
},
scaleLabel: {
display: true,
labelString: 'Slow SQL Queries'
},
id: "y-axis-1"
}]
},
tooltips: {
enabled: true,
mode: 'single',
callbacks: {
title: function(tooltipItem, data) {
return data.label;
},
beforeLabel: function(tooltipItem, data) {
if (tooltipItem.index == 24) {
return data.labels[tooltipItem.index] + " - Now";
} else {
return data.labels[tooltipItem.index] + " - " + data.labels[(tooltipItem.index) + 1];
}
}
}
}
}
var ctx = document.getElementById("SQLPerformanceChart");
var SQLPerformanceChart = new Chart(ctx, {
type: 'bar',
data: data,
options: options
});
</script>
You can add a callback function to legends onClick:
var options = {
animation: false,
scales: {
yAxes: [{
position: "left",
ticks: {
beginAtZero: true
},
scaleLabel: {
display: true,
labelString: 'Total SQL Queries'
},
id: "y-axis-0"
}, {
position: "right",
ticks: {
beginAtZero: true
},
scaleLabel: {
display: true,
labelString: 'Slow SQL Queries'
},
id: "y-axis-1"
}]
},
legend: {
onClick: function(event, legendItem) {
//get the index of the clicked legend
var index = legendItem.datasetIndex;
//toggle chosen dataset's visibility
SQLPerformanceChart.data.datasets[index].hidden =
!SQLPerformanceChart.data.datasets[index].hidden;
//toggle the related labels' visibility
SQLPerformanceChart.options.scales.yAxes[index].display =
!SQLPerformanceChart.options.scales.yAxes[index].display;
SQLPerformanceChart.update();
}
}
}
This solution applies if you are using angular-chartjs, and if you want to apply this behaviour to all displayed charts.
If you want to skip to the code, check this fiddlejs.
You can also check this other fiddlejs to check the default Angular-Chartjs behaviour.
Step by step:
I use the first chart example in angular-chart.js, so this will be the final result after clicking:
<div ng-app="app" ng-controller="MainController as mainCtrl">
<canvas id="line" class="chart chart-line" chart-data="data"
chart-labels="labels" chart-series="series" chart-options="options"
chart-dataset-override="datasetOverride" chart-click="onClick">
</canvas>
</div>
Replace the handler of the global Chart:
Chart.defaults.global.legend.onClick = function (e, legendItem) {
var idx = legendItem.datasetIndex;
// IMPORTANT charts will be created in the second and third step
var chart = charts[e.srcElement.id];
chart.options.scales.yAxes[idx].display = !chart.options.scales.yAxes[idx].display;
var meta = chart.getDatasetMeta(idx);
// See controller.isDatasetVisible comment
meta.hidden = meta.hidden === null ? !chart.data.datasets[idx].hidden : null;
chart.update();
};
Create a global variable charts so we can get access each of the charts with the canvas id:
var charts = {};
Fill up the charts variables using the chart-create event:
angular.module("app", ["chart.js"]).controller("MainController", function ($scope) {
$scope.$on('chart-create', function (event, chart) {
charts[chart.chart.canvas.id] = chart;
});
$scope.labels = ["January", "February", "March", "April", "May", "June", "July"];
$scope.series = ['Series A', 'Series B'];
$scope.data = [...
I wish there would be a better way of getting a chart from the canvas id, but as far as I know this is the suggested way by the developers.
This solution applies if you are using ng2-charts with chart.js and Angular 7^ and if you want to apply this behavior to all displayed charts.
import Chart from chart.js
Chart.defaults.global.legend.onClick = function (e: MouseEvent, chartLegendLabelItem: ChartLegendLabelItem) {
const idx: number = chartLegendLabelItem.datasetIndex;
const chart = this.chart;
chart.options.scales.yAxes[idx].display = !chart.options.scales.yAxes[idx].display;
const meta = chart.getDatasetMeta(idx);
meta.hidden = meta.hidden === null ? !chart.data.datasets[idx].hidden : null;
chart.update();
};
or for local configuration
legend: <ChartLegendOptions>{
onClick: function (e: MouseEvent, chartLegendLabelItem:ChartLegendLabelItem) {
const idx: number = chartLegendLabelItem.datasetIndex;
const chart = this.chart;
chart.options.scales.yAxes[idx].display =
!chart.options.scales.yAxes[idx].display;
const meta = chart.getDatasetMeta(idx);
meta.hidden = meta.hidden === null ?
!chart.data.datasets[idx].hidden : null;
chart.update();
}
}
I came along this problem using v3.8.0, none of the obove worked for me.
This code works for me.
Note I'm storing all my chart instances in a Map because I have multiple charts on the same page.
var instances = new Map();
When createing the incances I put them there.
and now the hiding of the y axis label and data on legend click:
onClick: function (event, legendItem) {
var instance = instances.get(event.chart.id);
var meta = instance.getDatasetMeta(legendItem.datasetIndex);
var newValue = !meta.yScale.options.display;
meta.hidden = meta.yScale.options.display;
meta.yScale.options.display = newValue;
instance.update();
}

Categories