Related
Herewith I used JavaScript line chart within Laravel framework. Mouse hover tool tip to display point values working perfectly with label:function(...). I need to display only y-axis value on every point at chart on create (Without mouse hovering). To perform this I used drawDatasetPointsLabels() method from How to display Line Chart dataset point labels with Chart.js? and call it on chart options. But unfortunately it is not working. Below is the code.
chart.blade
monthlabels={.......};
salesqty = {......};
stockqty = {......};
document.getElementById("history_canvas_holder").innerHTML = ' ';
document.getElementById("history_canvas_holder").innerHTML = '<canvas id="historyChart" width="500" height="350"></canvas>';
var ctx = document.getElementById("historyChart").getContext('2d');
var myLineChart = new Chart(ctx,
{
type: 'line',
data:
{
labels: monthlabels,
datasets: [{
label: 'Sales',
data: salesqty,
borderColor: [
'rgba(255,99,132,1)'
],
borderWidth: 2
},
{
label: 'Stock',
data: stockqty,
borderColor: [
'rgba(0,161,232)'
],
borderWidth: 2
}
]
},
options: {
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var label = data.datasets[tooltipItem.datasetIndex].label || '';
if (label) {
label += ': ';
}
label += Math.round(tooltipItem.yLabel * 100) / 100;
return label;
},
onAnimationProgress: function() { drawDatasetPointsLabels(ctx) },
onAnimationComplete: function() { drawDatasetPointsLabels(ctx) }
}
}
}
});
function drawDatasetPointsLabels(ctx) {
ctx.font = '.9rem "ABCD",sans-serif';
ctx.fillStyle = '#AAA';
ctx.textAlign="center";
$(historyChart.datasets).each(function(idx,dataset){
$(dataset.points).each(function(pdx,pointinfo){
if ( pointinfo.value !== null ) {
ctx.fillText(pointinfo.value,pointinfo.x,pointinfo.y - 15);
}
});
});
}
In the type of v2.5.0
`options: {
tooltips: {
enabled: false
}
}`
or
`tooltips :{
custom : function(tooltipModel) {
tooltipModel.opacity = 0;
}
}`
type of v2.1.4
`Chart.defaults.global.tooltips.enabled = false;`
people said those ways are work, but not in all charts type.
I'm a complete newb with js and jquery but have muddled my way through getting a chart to properly display using Flask and an Ajax request. Where I'm having a problem is getting the charts data to refresh. I can get the chart to display just fine if I create it as a new chart as shown in the code below
$(document).ready(function() {
var getdata = $.post('/charts');
var ctx = document.getElementById('myChart').getContext('2d');
var myChart
getdata.done(function(results){
var chartData = {
labels: results['month'],
datasets: [{
label: 'Debits',
data: results['debit'],
backgroundColor: "rgba(153,255,51,0.4)"
}, {
label: 'Credits',
data: results['credit'],
backgroundColor: "rgba(255,153,0,0.4)"
}, {
label: 'Balance',
data: results['balance'],
backgroundColor: "rgba(50,110,0,0.4)"
}]
}
myChart = new Chart(ctx, {type: 'line', data: chartData});
});
$("form :input").change(function() {
year = $(this).val();
console.log(year)
$.ajax({
type: "POST",
url: "/data",
data: {'year':year},
success: function(results){
var updatedData = {
labels : results['month'],
datasets : [{
label: 'Debits',
data: results['debit'],
backgroundColor: "rgba(153,255,51,0.4)"
}, {
label: 'Credits',
data: results['credit'],
backgroundColor: "rgba(255,153,0,0.4)"
}, {
label: 'Balance',
data: results['balance'],
backgroundColor: "rgba(50,110,0,0.4)"
}]
}
myChart= new Chart(ctx, {type: 'line', data: updatedData});
}
});
});
});
But if I change the last line to
myChart.update(updatedData)
nothing happens, I don't get any errors, the chart doesn't update. Nothing. The strange thing is that I know the myChart is global because I don't have to create it again after the Ajax request.
Thanks
From the documentation of charts.js (http://www.chartjs.org/docs/#getting-started-creating-a-chart), you first need to change data and then call update (arguments for update function are not the data, but duration etc.):
.update(duration, lazy)
Triggers an update of the chart. This can be safely called after replacing the entire data object. This will update all scales, legends, and then re-render the chart.
// duration is the time for the animation of the redraw in milliseconds
// lazy is a boolean. if true, the animation can be interrupted by other animations
myLineChart.data.datasets[0].data[2] = 50; // Would update the first dataset's value of 'March' to be 50
myLineChart.update(); // Calling update now animates the position of March from 90 to 50.
So in your case:
myChart.data = updatedData;
myChart.update();
I've just started working with Chart.js, and I am getting very frustrated very quickly. I have my stacked bar chart working, but I can't get the click "events" to work.
I have found a comment on GitHub by nnnick from Chart.js stating to use the function getBarsAtEvent, even though this function cannot be found in the Chart.js documentation at all (go ahead, do a search for it). The documentation does mention the getElementsAtEvent function of the chart reference, but that is for Line Charts only.
I set an event listener (the right way) on my canvas element:
canv.addEventListener('click', handleClick, false);
...yet in my handleClick function, chart.getBarsAtEvent is undefined!
Now, in the Chart.js document, there is a statement about a different way to register the click event for the bar chart. It is much different than nnnick's comment on GitHub from 2 years ago.
In the Global Chart Defaults you can set an onClick function for your chart. I added an onClick function to my chart configuration, and it did nothing...
So, how the heck do I get the on-click-callback to work for my bar chart?!
Any help would be greatly appreciated. Thanks!
P.S.: I am not using the master build from GitHub. I tried, but it kept screaming that require is undefined and I was not ready to include CommonJS just so that I could use this chart library. I would rather write my own dang charts. Instead, I downloaded and am using the Standard Build version that I downloaded straight from the link at the top of the documentation page.
EXAMPLE: Here is an example of the configuration I am using:
var chart_config = {
type: 'bar',
data: {
labels: ['One', 'Two', 'Three'],
datasets: [
{
label: 'Dataset 1',
backgroundColor: '#848484',
data: [4, 2, 6]
},
{
label: 'Dataset 2',
backgroundColor: '#848484',
data: [1, 6, 3]
},
{
label: 'Dataset 3',
backgroundColor: '#848484',
data: [7, 5, 2]
}
]
},
options: {
title: {
display: false,
text: 'Stacked Bars'
},
tooltips: {
mode: 'label'
},
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [
{
stacked: true
}
],
yAxes: [
{
stacked: true
}
]
},
onClick: handleClick
}
};
I managed to find the answer to my question by looking through the Chart.js source code.
Provided at line 3727 of Chart.js, Standard Build, is the method .getElementAtEvent. This method returns me the "chart element" that was clicked on. There is sufficent data here to determine what data to show in a drill-down view of the dataset clicked on.
On the first index of the array returned by chart.getElementAtEvent is a value _datasetIndex. This value shows the index of the dataset that was clicked on.
The specific bar that was clicked on, I believe, is noted by the value _index. In my example in my question, _index would point to One in chart_config.data.labels.
My handleClick function now looks like this:
function handleClick(evt)
{
var activeElement = chart.getElementAtEvent(evt);
..where chart is the reference of the chart created by chart.js when doing:
chart = new Chart(canv, chart_config);
The specific set of data that was selected by the click can therefore be found as:
chart_config.data.datasets[activeElement[0]._datasetIndex].data[activeElement[0]._index];
And there you have it. I now have a datapoint that I can build a query from to display the data of the bar that was clicked on.
AUGUST 7TH, 2021. UPDATE
There is now a method for what we are looking for. Take a look at here
Hi this is the click event under options which is getting values from x and y-axis
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);
}
I found this solution at https://github.com/valor-software/ng2-charts/issues/489
public chartClicked(e: any): void {
if (e.active.length > 0) {
const chart = e.active[0]._chart;
const activePoints = chart.getElementAtEvent(e.event);
if ( activePoints.length > 0) {
// get the internal index of slice in pie chart
const clickedElementIndex = activePoints[0]._index;
const label = chart.data.labels[clickedElementIndex];
// get value by index
const value = chart.data.datasets[0].data[clickedElementIndex];
console.log(clickedElementIndex, label, value)
}
}
}
You can use onClick like this.
var worstCells3GBoxChart = new Chart(ctx, {
type: 'bar',
data: {
labels: lbls,
datasets: [{
label: 'Worst Cells by 3G',
data: datas,
backgroundColor: getColorsUptoArray('bg', datas.length),
borderColor: getColorsUptoArray('br', datas.length),
borderWidth: 1
}]
},
options: {
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
},
onClick: function (e) {
debugger;
var activePointLabel = this.getElementsAtEvent(e)[0]._model.label;
alert(activePointLabel);
}
}
});
Chartjs V3.4.1
This is what worked for me in v3, after looking at solutions for older versions:
const onClick = (event, clickedElements) => {
if (clickedElements.length === 0) return
const { dataIndex, raw } = clickedElements[0].element.$context
const barLabel = event.chart.data.labels[dataIndex]
...
}
raw is the value of the clicked bar.
barLabel is the label of the clicked bar.
You need to pass the onClick to the bar chart config:
const barConfig = {
...
options: {
responsive: true,
onClick,
...
}
}
Well done! This seems to return the data value being charted though, which in many cases might be possible to appear more than once, thus making it unclear what was clicked on.
This will return the actual data label of the bar being clicked on. I found this more useful when drilling down into a category.
chart_config.data.labels[activeElement[0]._index]
I was able to make this work in another way.
Might not be supported, but sometimes, I find that neither the label nor the value is adequate to get me the necessary information to populate a drill-through.
So what I did was add a custom set of attributes to the data:
var ctx = document.getElementById("cnvMyChart").getContext("2d");
if(theChart != null) {theChart.destroy();}
theChart = new Chart(ctx, {
type: typ,
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datakeys: ["thefirstone","thesecondone","thethirdone","thefourthone","thefifthone","thesixthone"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
...etc
Then when I need to push the drillthrough key into another ajax call, I was able to get it with this:
var theDrillThroughKey = theChart.config.data.datakeys[activePoints[0]._index];
So I'm really not sure that it's appropriate to be adding custom elements into the data for the Chart, but it's working so far in Chrome, IE and Firefox. I needed to be able to put more information into the drillthrough than I really wanted displayed.
Example of the full thing: https://wa.rrdsb.com/chartExamples
Thoughts?
I had the same problem with multiple datasets, and used this workaround:
var clickOnChart = function(dataIndex){
...
}
var lastHoveredIndex = null;
var chart_options = {
...
tooltips: {
...
callbacks: {
label: function(tooltipItem, chart) {
var index = tooltipItem.datasetIndex;
var value = chart.datasets[index].data[0];
var label = chart.datasets[index].label;
lastHoveredIndex = index;
return value + "€";
}
}
},
onClick:function(e, items){
if ( items.length == 0 ) return; //Clicked outside any bar.
clickOnChart(lastHoveredIndex);
}
}
Let's say that you declared a chart using a method like so:
window.myBar = new Chart({chart_name}, {
type: xxx,
data: xxx,
events: ["click"],
options: {
...
}
});
A good way of declaring onclick events would involve listening for the canvas click, like so:
({chart_name}.canvas).onclick = function(evt) {
var activePoints = myBar.getElementsAtEvent(evt);
// let's say you wanted to perform different actions based on label selected
if (activePoints[0]._model.label == "label you are looking for") { ... }
}
In the chart options for Chart.js v3.5.1 which is latest
Check below sample code
let enterpriseChartOptions = {
responsive:true,
maintainAspectRatio: false,
onClick: (c,i) => {
console.log('Get the underlying label for click,', c.chart.config._config.data.labels[i[0].index]);
},
plugins: {
title:{
text:'Enterprise Dashboard (Health Status of 10 stores) updated every 30 minutes',
fontSize:20
},
},
scales: {
x: {
display: true,
type: 'category',
position: 'right',
ticks: {
padding: 8,
},
},
y: {
display: true,
ticks: {
callback: function(val, index) {
// Show the label
return val < 1 ? "All good" : (val < 2 && val >=1) ? "Warning": val === 2 ? "Critical" : "";
},
//color: 'red',
stepSize: 1,
padding: 8
}
}
},
layout: {
padding: {
left: 20,
right: 20,
top: 25,
bottom: 0
}
},
};
var employeeDetailsCtx = document.getElementById("employee-details").getContext("2d");
var employee_details_data = {
labels: ["Late Present", "On Leave", "Training", "Tour"],
datasets: [{
label: "Officer",
backgroundColor: "#5A8DEE",
data: [
...
]
}, {
label: "Staff",
backgroundColor: "#4BC0C0",
data: [
...
]
}]
};
var myoption = {
tooltips: {
enabled: true
},
hover: {
animationDuration: 1
},
onClick: function (evt, i) {
var activePoint = employeeDetailsBarChart.getElementAtEvent(evt)[0];
var data = activePoint._chart.data;
var datasetIndex = activePoint._datasetIndex;
var label = data.datasets[datasetIndex].label;
var value = data.datasets[datasetIndex].data[activePoint._index];
e = i[0];
var x_value = this.data.labels[e._index];
console.log(x_value)
console.log(label)
console.log(value)
},
animation: {
duration: 1,
onComplete: function () {
var chartInstance = this.chart,
ctx = chartInstance.ctx;
ctx.textAlign = 'center';
ctx.fillStyle = "rgba(0, 0, 0, 1)";
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);
});
});
}
}
};
var employeeDetailsBarChart = new Chart(employeeDetailsCtx, {
type: 'bar',
data: employee_details_data,
options: myoption
});
I am generating a pie chart from data stored in JSON format. I am trying to change color according to the JSON value.
Ex : if value # json[0]['data'][0][0] = "FAILED" //setColor(RED).
I was able to set the color for column stack charts using options.series.color, however when I tried to use this option with pie chart its converting data into series and unable to render the chart on a container.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
function getData(id) {
$.getJSON("pie.php", {
id: id
}, function(json) {
option.series = json;
chart = new Highcharts.chart(options);
});
}
</script>
can we set the color in getData function only before calling 'chart' or do i need to use Highcharts.setOptions() and define the color codes.
The better option is to create series based on your json data. This is how you can do to specify color based on data.
var serie = {
data: []
};
var series = [serie];
jQuery.each(jsonData, function(index, pointData) {
var point = {
name: pointName,
y: pointData.Value,
color: pointData.Value == 'FAILED' ? 'ff0000' : '00ff00',
serverData: pointData
};
serie.data.push(point);
});
chart.series = series;
OR
Have a look at this easier version
JSFiddle
$( document ).ready(function() {
var data = [{
"name": "Tokyo",
"data": 3.0
}, {
"name": "NewYork",
"data": 2.0
}, {
"name": "Berlin",
"data": 3.5
}, {
"name": "London",
"data": 1.5
}];
// Highcharts requires the y option to be set
$.each(data, function (i, point) {
point.y = point.data;
point.color = parseFloat(point.data) > 3 ? '#ff0000' : '#00ff00';
});
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'pie'
},
series: [{
data: data
}]
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<div id="container" style="height: 300px"></div>
we can set highchart custom color by setOption function which is as
Highcharts.setOptions({
colors: ['#F64A16', '#0ECDFD',]
});
It sets color to my pie chart.
Another solution for dynamic 3D color
Actually this customization for theme selection Here it is
3 colors sets to color variable
var colors = Highcharts.getOptions().colors;
$.each(colors, function(i, color) {
colors[i] = {
linearGradient: { x1: 0, y1: 0, x2: 1, y2: 0 },
stops: [
[0, '#0ECDFD'],
[0.3, '#F64A16'],
[1, color]
]
};
});
& assign directly in series
{
type : 'column',
name : 'bug',
data : [],
color : colors,
pointWidth : 28,
}
I'm attempting to combine a couple of different chart demos from Highcharts.
My examples are: Data classes and popup and Small US with data labels
I want the map from the first with the popup feature of the second. I need to connect the map to my own google spreadsheet but for now I'm just trying to get the data from the first example to work.
This is what I have so far but can't seem to get any data in the map. I thought I had a joinBy problem, and I may still, but when I set joinBy to null I thought "the map items are joined by their position in the array", yet nothing happened.
https://jsfiddle.net/9eq6mydv/
$(function () {
// Load the data from a Google Spreadsheet
// https://docs.google.com/a/highsoft.com/spreadsheet/pub?hl=en_GB&hl=en_GB&key=0AoIaUO7wH1HwdFJHaFI4eUJDYlVna3k5TlpuXzZubHc&output=html
Highcharts.data({
googleSpreadsheetKey: '0AoIaUO7wH1HwdDFXSlpjN2J4aGg5MkVHWVhsYmtyVWc',
googleSpreadsheetWorksheet: 1,
// custom handler for columns
parsed: function (columns) {
// Make the columns easier to read
var keys = columns[0],
names = columns[1],
percent = columns[10],
// Initiate the chart
options = {
chart : {
renderTo: 'container',
type: 'map',
borderWidth : 1
},
title : {
text : 'US presidential election 2008 result'
},
subtitle: {
text: 'Source: <a href="http://en.wikipedia.org/wiki/United_States_presidential_election,' +
'_2008#Election_results">Wikipedia</a>'
},
mapNavigation: {
enabled: true,
enableButtons: false
},
legend: {
align: 'right',
verticalAlign: 'top',
x: -100,
y: 70,
floating: true,
layout: 'vertical',
valueDecimals: 0,
backgroundColor: (Highcharts.theme && Highcharts.theme.legendBackgroundColor) || 'rgba(255, 255, 255, 0.85)'
},
colorAxis: {
dataClasses: [{
from: -100,
to: 0,
color: '#C40401',
name: 'McCain'
}, {
from: 0,
to: 100,
color: '#0200D0',
name: 'Obama'
}]
},
series : [{
data : data,
dataLabels: {
enabled: true,
color: '#FFFFFF',
format: '{point.code}',
style: {
textTransform: 'uppercase'
}
},
mapData: Highcharts.geojson(Highcharts.maps['countries/us/custom/us-small']),
joinBy: keys,
name: 'Democrats margin',
point: {
events: {
click: pointClick
}
},
tooltip: {
ySuffix: ' %'
},
cursor: 'pointer'
}, {
type: 'mapline',
data: Highcharts.geojson(Highcharts.maps['countries/us/custom/us-small'], 'mapline'),
color: 'silver'
}]
};
/**
* Event handler for clicking points. Use jQuery UI to pop up
* a pie chart showing the details for each state.
*/
function pointClick() {
var row = this.options.row,
$div = $('<div></div>')
.dialog({
title: this.name,
width: 400,
height: 300
});
window.chart = new Highcharts.Chart({
chart: {
renderTo: $div[0],
type: 'pie',
width: 370,
height: 240
},
title: {
text: null
},
series: [{
name: 'Votes',
data: [{
name: 'Obama',
color: '#0200D0',
y: parseInt(columns[3][row], 10)
}, {
name: 'McCain',
color: '#C40401',
y: parseInt(columns[4][row], 10)
}],
dataLabels: {
format: '<b>{point.name}</b> {point.percentage:.1f}%'
}
}]
});
}
// Read the columns into the data array
var data = [];
$.each(keys, function (i, key) {
data.push({
key: key,//.toUpperCase(),
value: parseFloat(percent[i]),
name: names,
row: i
});
});
// Initiate the chart
window.chart = new Highcharts.Map(options);
},
error: function () {
$('#container').html('<div class="loading">' +
'<i class="icon-frown icon-large"></i> ' +
'Error loading data from Google Spreadsheets' +
'</div>');
}
});
});
UPDATE:
I wanted to share with everyone my final solution. Although Ondkloss did a magnificent job answering my question the popup feature still didn't work and this is because I forgot to include the jQuery for the .dialog call. Once I included that I had an empty popup with a highchart error 17, this is because the highmaps.js code doesn't include the pie chart class. So I had to add the highcharts.js code and include map.js module afterward. You can see my final jsfiddle here.
Thanks again to Ondkloss for the excellent answer!
The problem here mostly comes down to the use of joinBy. Also to correct it there are some required changes to your data and mapData.
Currently your joinBy is an array of strings, like ["al", "ak", ...]. This is quite simply not an accepted format of the joinBy option. You can read up on the details in the API documentation, but the simplest approach is to have a attribute in common in data and mapData and then supply a string in joinBy which then joins those two arrays by that attribute. For example:
series : [{
data : data,
mapData: mapData,
joinBy: "hc-key",
]
Here the "hc-key" attribute must exist in both data and mapData.
Here's how I'd create the data variable in your code:
var data = [];
$.each(keys, function (i, key) {
if(i != 0)
data.push({
"hc-key": "us-"+key,
code: key.toUpperCase(),
value: parseFloat(percent[i]),
name: names[i],
row: i
});
});
This skips the first key, which is just "Key" (the title of the column). Here we make the "hc-key" fit the format of the "hc-key" in our map data. An example would be "us-al". The rest is just metadata that will be joined in. Note that you were referencing your data in the options prior to filling it with data, so this has to be moved prior to this.
This is how I'd create the mapData variable in your code:
var mapData = Highcharts.geojson(Highcharts.maps['countries/us/custom/us-small']);
// Process mapdata
$.each(mapData, function () {
var path = this.path,
copy = { path: path };
// This point has a square legend to the right
if (path[1] === 9727) {
// Identify the box
Highcharts.seriesTypes.map.prototype.getBox.call(0, [copy]);
// Place the center of the data label in the center of the point legend box
this.middleX = ((path[1] + path[4]) / 2 - copy._minX) / (copy._maxX - copy._minX);
this.middleY = ((path[2] + path[7]) / 2 - copy._minY) / (copy._maxY - copy._minY);
}
// Tag it for joining
this.ucName = this.name.toUpperCase();
});
The first part is your "standard map data". The rest is to correctly center the labels for the popout states, and gotten directly from the example.
And voila, see this JSFiddle demonstration to witness your map in action.
I suggest doing some console.log-ing to see how data and mapData have the hc-key in common and that leads to the joining of the data in the series.