JavaScript Chart.js - Custom data formatting to display on tooltip - javascript

I have looked at various documentation and similar questions on here, but cannot seem to find the particular solution. Apologies if I have missed anything obvious or have repeated this question!
As a bit of background info, I have implemented 4 graphs using the Chart.js plugin and passed in the required data using PHP from a database. This is all working correctly and is fine.
My problem is I need to display the data in the tooltips as formatted data aka. as numeric with %. As an example, one of my data from database is -0.17222. I have formatted it as a percentage to display in my table and all is well. However, when setting the data for the chart.js bar graph, the data is obviously missing this formatting and displaying as the -0.17222 which I do not want.
Sorry, I wanted to post a picture, but my reputation is too rubbish!
I grab data from database, then set into my table:
var kpiRex = new Handsontable(example1, {
data: getRexData(),
Then I feed this data like so in the chart section:
data: kpiRex.getDataAtRow(3)
Any help would be great! I've been stuck on this for hours and it's probably something really simple I am overlooking.

For chart.js 2.0+, this has changed (no more tooltipTemplate/multiTooltipTemplate). For those that just want to access the current, unformatted value and start tweaking it, the default tooltip is the same as:
options: {
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
return tooltipItem.yLabel;
}
}
}
}
I.e., you can return modifications to tooltipItem.yLabel, which holds the y-axis value. In my case, I wanted to add a dollar sign, rounding, and thousands commas for a financial chart, so I used:
options: {
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
return "$" + Number(tooltipItem.yLabel).toFixed(0).replace(/./g, function(c, i, a) {
return i > 0 && c !== "." && (a.length - i) % 3 === 0 ? "," + c : c;
});
}
}
}
}

You want to specify a custom tooltip template in your chart options, like this :
// String - Template string for single tooltips
tooltipTemplate: "<%if (label){%><%=label %>: <%}%><%= value + ' %' %>",
// String - Template string for multiple tooltips
multiTooltipTemplate: "<%= value + ' %' %>",
This way you can add a '%' sign after your values if that's what you want.
Here's a jsfiddle to illustrate this.
Note that tooltipTemplate applies if you only have one dataset, multiTooltipTemplate applies if you have several datasets.
This options are mentioned in the global chart configuration section of the documentation. Do have a look, it's worth checking for all the other options that can be customized in there.
Note that Your datasets should only contain numeric values. (No % signs or other stuff there).

In chart.js 2.1.6, I did something like this (in typescript):
let that = this;
options = {
legend: {
display: false,
responsive: false
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
let account: Account = that.accounts[tooltipItem.index];
return account.accountNumber+":"+account.balance+"€";
}
}
}
}

You can give tooltipTemplate a function, and format the tooltip as you wish:
tooltipTemplate: function(v) {return someFunction(v.value);}
multiTooltipTemplate: function(v) {return someOtherFunction(v.value);}
Those given 'v' arguments contain lots of information besides the 'value' property. You can put a 'debugger' inside that function and inspect those yourself.

tooltips: {
callbacks: {
label: (tooltipItem, data) => {
// data for manipulation
return data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
},
},
},

This works perfectly fine with me. It takes label and format the value.
options: {
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
let label = data.labels[tooltipItem.index];
let value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
return ' ' + label + ': ' + value + ' %';
}
}
}
}

tooltips: {
callbacks: {
label: function (tooltipItem) {
return (new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
})).format(tooltipItem.value);
}
}
}

In Chart.Js 2.8.0, the configuration for custom tooltips can be found here: https://www.chartjs.org/docs/latest/configuration/tooltip.html#label-callback (Thanks to #prokaktus)
If you want to e.g. show some values with a prefix or postfix (In the example, the script adds a unit of kWh to the values in the chart), you could do this like:
options: {
rotation: 1 * Math.PI,
circumference: 1 * Math.PI,
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
console.log(data);
console.log(tooltipItem);
var label = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] || '';
if (label) {
label += ' kWh';
}
return label;
}
}
}
}
An example fiddle is here, too: https://jsfiddle.net/y3petw58/1/

This is what my final options section looks like for chart.js version 2.8.0.
options: {
legend: {
display: false //Have this or else legend will display as undefined
},
scales: {
//This will show money for y-axis labels with format of $xx.xx
yAxes: [{
ticks: {
beginAtZero: true,
callback: function(value) {
return (new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
})).format(value);
}
}
}]
},
//This will show money in tooltip with format of $xx.xx
tooltips: {
callbacks: {
label: function (tooltipItem) {
return (new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
})).format(tooltipItem.value);
}
}
}
}
I wanted to show money values for both the y-axis and the tooltip values that show up when you hover over them. This works to show $49.99 and values with zero cents (ex: $50.00)

The answers here did not work for me on Char.js 3.8.0. The tooltip options callbacks object has moved, apparently. Now it is under options.plugins.tooltip
https://www.chartjs.org/docs/latest/configuration/tooltip.html
Example for OP:
options: {
plugins: {
tooltip: {
callbacks: {
label: function (tooltipItem, data) {
console.log(data);
console.log(tooltipItem);
return tooltipItem.formattedValue + '%';
}
}
}
},
...

tooltips: {
enabled: true,
mode: 'single',
callbacks: {
label: function(tooltipItems, data) {
return data.datasets[tooltipItems.datasetIndex].label+": "+tooltipItems.yLabel;
}
}
}

You need to make use of Label Callback.
A common example to round data values, the following example rounds the data to two decimal places.
var chart = new Chart(ctx, {
type: 'line',
data: data,
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;
}
}
}
}
});
Now let me write the scenario where I used the label callback functionality.
Let's start with logging the arguments of Label Callback function, you will see structure similar to this here datasets, array comprises of different lines you want to plot in the chart.
In my case it's 4, that's why length of datasets array is 4.
In my case, I had to perform some calculations on each dataset and have to identify the correct line, every-time I hover upon a line in a chart.
To differentiate different lines and manipulate the data of hovered tooltip based on the data of other lines I had to write this logic.
callbacks: {
label: function (tooltipItem, data) {
console.log('data', data);
console.log('tooltipItem', tooltipItem);
let updatedToolTip: number;
if (tooltipItem.datasetIndex == 0) {
updatedToolTip = tooltipItem.yLabel;
}
if (tooltipItem.datasetIndex == 1) {
updatedToolTip = tooltipItem.yLabel - data.datasets[0].data[tooltipItem.index];
}
if (tooltipItem.datasetIndex == 2) {
updatedToolTip = tooltipItem.yLabel - data.datasets[1].data[tooltipItem.index];
}
if (tooltipItem.datasetIndex == 3) {
updatedToolTip = tooltipItem.yLabel - data.datasets[2].data[tooltipItem.index]
}
return updatedToolTip;
}
}
Above mentioned scenario will come handy, when you have to plot different lines in line-chart and manipulate tooltip of the hovered point of a line, based on the data of other point belonging to different line in the chart at the same index.

Related

how do i set json data set as labels for graph plot with c3.js

I have a line graph generated with c3.js with json data
the current chart is very simple
var chart = c3.generate({
bindto: '.balanceChart',
data: {
url: '/data',
mimeType:'json'
}
});
json data:
{
data1: [1000,1240,1270,1250,1280]
data2: [1000,240,30,-20,30]
}
chart looks good and is there
but it is currently plotting both sets of data
what i would like is for data2 to be the tooltip value of the plot
You can hide data2 from displaying like so
data: {
...
hide: ['data2']
}
From http://c3js.org/reference.html#data-hide
And use tooltip.format.value to change the tooltip display
tooltip: {
format: {
value: function (value, ratio, id, index) {
// return chart.data.values("data2")[index]; // if still wanting to use data2
// or get rid of data2 completely using this
var vals = chart.data.values(id); // id will be 'data1', vals will then be data1 array
return vals[index] - (index === 0 ? 0 : vals[index - 1]);
}
}
}
http://c3js.org/reference.html#tooltip-format-value
tooltip.format.title and tooltip.format.name will also be useful here to communicate to a user the value isn't actually that of data1 (maybe just changing the title to "Delta Data1")

Update HighCharts chart when AJAX Updates my data on my page

I have a Page where I have some Project Stats based on different Project Task Statuses. On this page I use AJAX to update my Stat values as they change.
I am now trying to integrate a Highcharts bar chart/graph and I need to update it;s chart when my data changes.
There is a JSFiddle here showing the chart I am experimenting with now http://jsfiddle.net/jasondavis/9dr345og/1/
$(function () {
$('#container').highcharts({
data: {
table: document.getElementById('datatable')
},
chart: {
type: 'column'
},
title: {
text: 'Project Stats'
},
yAxis: {
allowDecimals: false,
title: {
text: 'Total'
}
},
tooltip: {
formatter: function () {
return '<b>' + this.series.name + '</b><br/>' +
this.point.y + ' ' + this.point.name.toLowerCase();
}
},
subtitle: {
enabled: true,
text: 'Project Stats'
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
credits: {
enabled: false
}
});
// Button Click to Simulate my Data updating. This increments the Completed Tasks bar by 1 on each click.
$(".update").click(function() {
var completedVal = $('#completed').text();
++completedVal
$('#completed').text(completedVal)
});
});
So this example is getting the data from a Table but I do not have to use this method, I could also set it with JavaScript if needed.
I just need to figure out how I can update all these values on the fly as my real live page updates my task stat values using AJAX so I would like this chart to update live as well.
Any help on how to make it update? When my AJAX code is ran, I could call some JavaScript at that point if there is a function that rebuilds the chart?
I would drop the use of the table, especially since it looks like you are building it just for highcharts to consume it. Instead return your data via AJAX as a Highcharts series object. and then use the Series.setData method to update your plot. This would be the right way to do it.
If you really want to use the table, you could query out the data and still use setData (this is what Highcharts is doing for you under the hood). Updated fiddle.
$(".update").click(function() {
var completedVal = $('#completed').text();
++completedVal;
$('#completed').text(completedVal);
// get y values
var yValues = $.map($('#datatable tr td'),function(i){return parseFloat($(i).text());});
// set data
Highcharts.charts[0].series[0].setData(yValues);
});

How to display a multi-line title in a Highslide pop-up while using Highcharts?

I'm using Highcharts to display a chart and I'm using Highslide to display additional information in a pop-up when the user clicks on a point in the chart. However, I want to add additional information to the title/heading-text in the pop-up.
I've gotten Highslide to work in displaying basic information using the following code:
$(function () {
var options = {
chart: {
renderTo: 'container',
defaultSeriesType: 'line'
}
credits: {
enabled: false
}
plotOptions: {
series: {
cursor: 'pointer',
point: {
events: {
click: function() {
hs.htmlExpand(null, {
pageOrigin: {
x: this.pageX,
y: this.pageY
},
headingText: this.series.name,
maincontentText: Highcharts.dateFormat('%A, %b %e, %Y', this.x) +':<br/> '+
this.y +' visits',
width: 200
});
}
}
},
marker: {
lineWidth: 1
}
}
},
series: []
}
});
I've read through the API for Highslide and saw that you could use the 'heading' variable with the 'Expander.prototype.onAfterGetHeading' function, which I've displayed below, but I'm not sure how to implement it with Highcharts.
hs.Expander.prototype.onAfterGetHeading = function () {
this.heading.html = 'this.a.title';
}
HERE's a site that displays a pop-up with a multi-line title in a Highslide pop-up, as an example. However, keep in mind that I'm trying to implement this in Highcharts, possibly with dynamic text in the title.
Thanks.
Here’s a solution you can use with Highcharts: http://jsfiddle.net/roadrash/gd3xz/
Please note that this is the correct code to insert the anchor's title text in the heading, as an additional heading text:
hs.Expander.prototype.onAfterGetHeading = function (sender) {
if (sender.heading) {
sender.heading.innerHTML += this.a.title;
}
};
I’m not familiar with Highcharts, but I don’t think you can use this.a.title since a refers to the anchor tag. You can of course insert something else than this.a.title with the above code, as I've done in my demo.

Highcharts - how to toggle datalabels via js?

I would like to use Highcharts in such a way where clicking on a legend item enables datalabels for that particular category. Is this possible? I have tried the following to enable datalabels with no luck:
chart.series[0].data.dataLabels.enabled = true;
Here's a jsfiddle I am working with: http://jsfiddle.net/MXZgj/3/ (please search for legendItemClick to see where the applicable code is).
Thank you for your help!
This works:
chart.series[0].update({ dataLabels: { enabled:true }})
I ended up having to iterate over every point in the series and enable each individual data label.
series: {
events: {
legendItemClick: function(event) {
var selected = this.index;
var allSeries = this.chart.series;
$.each(allSeries, function(index, series) {
if (selected == index) {
$.each(series.points, function(i, point) {
point.update({
dataLabels: {
enabled: true
}
});
});
} else {
$.each(series.points, function(i, point) {
point.update({
dataLabels: {
enabled: false
}
});
});
}
});
return false;
}
}
}
Could use a bit of code cleanup, but works great as is.

How do you make a bar a different color in KendoUI bar chart?

I'm am replacing my dot net charting with KendoUI. I'm showing a Score Distribution chart. I want all of the bars to be the same color except the bar with the median score and the Legend. How do I color a single bar a unique color? How would I color the Legend this new color?
Below is my old dotnet charting bar chart and below that is the new KendoUI chart I'm trying to replace it with. I just need to get that coloring right and we'll be in business. Any help is appreciated.
Update: I'm leaving the answer below this line intact in case there are those out there who are using an older version, but per a later comment, KendoUI now allows you to override styles for individual points in a series.
I don't believe you can, in the current version. That said, you can hack your way around the limitation.
You'll need to create two data series - one for your highlighted data, and one for everything else. Add both to you chart, and set them to stacked.
Here's a jsFiddle I put together to illustrate: http://jsfiddle.net/LyndsySimon/9VZdS/. It depends on KendoUI's CDN, so if it breaks in the future I apologize.
Here is the Javascript for reference:
$(function() {
var dataset = new Array(1,2,3,null,5,6);
var highlighted = new Array(null,null,null,4,null,null);
$('#chart').kendoChart({
theme: 'metro',
seriesDefaults: {
type: 'column',
stack: true
},
series: [
{
name: 'Not Highlighted',
data: dataset,
color: '#609'
},
{
name: 'Highlighted',
data: highlighted,
color: '#f03'
}
]
})
});​
Starting with the 2012 Q2 release, bar series support binding the point color to a data item field.
This is done through the colorField option. The Binding to local data online example demonstrates it.
Both the Kendo UI and the legacy wrappers for ASP.NET MVC expose it as an option:
.Series(series =>
{
series.Bar(model => model.Value, model => model.Color)
.Name("United States");
})
All series overloads can be seen here.
You could hack the SVG generated by the system. I have supplied the chart with a model which contains the colour for each bar. eg:
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Colour { get; set; }
public decimal Score { get; set; }
}
Which is used as a series on the chart. The view then looks like:
#(Html.Telerik().Chart(Model)
.Name("AverageScores")
.Theme("Simple")
.HtmlAttributes(new {style = "height: 500px"})
.Series(series => series.Column(s => s.Score).Name("Name").Color("Blue"))
.SeriesDefaults(sd => sd.Column().Labels(l =>
{
l.Visible(true);
l.Format("{0}%");
}))
.Title("Mean Percentage Scores")
.Legend(builder =>
{
builder.Position(ChartLegendPosition.Bottom);
builder.Visible(false);
})
.CategoryAxis(ca => ca.Categories(x => x.Code))
.Tooltip(builder =>
{
builder.Visible(true);
builder.Format("{0}%");
builder.Template("<#= dataItem.Name #><br/><#= value #>%");
})
.ValueAxis(va => va.Numeric().Labels(a => a.Format("{0}%")).Min(0).Max(100)
)
)
#section BelowTelerikScriptRegistrar
{
<script type="text/javascript">
function setAverageScoresColours() {
var data = $('#AverageScores').data("tChart").options.series[0].dataItems;
if (data != null) {
for (var i = 0; i < data.length; i++) {
var averageScore = data[i];
$($($('div#AverageScores svg g')[i]).children('path')[0]).attr('fill', '#' + averageScore.Colour);
$($($('div#AverageScores svg g')[i]).children('path')[0]).attr('stroke', '#' + averageScore.Colour);
}
}
}
$(document).ready(function () {
setAverageScoresColours();
})
</script>
}
The section BelowTelerikScriptRegistrar must happen after the Html.Telerik().ScriptRegistrar() is called.
This will work in Chrome, Firefox and IE10. I have noticed there is a problem with multiple chart and the timings around the generation of the SVG. Unfortunately you might have to wrap setAverageScoresColours() in a setTimeout function to ensure the SVG has been generated, but it seems to work with just one chart.
A bit hacky, but easier than managing lots of series.
And for KendoUI (which I have edited for...):
<div class="chart-wrapper">
<div id="chart"></div>
</div>
<script>
function createChart() {
$("#chart").kendoChart({
theme: $(document).data("kendoSkin") || "default",
title: {
text: "Internet Users"
},
legend: {
position: "bottom"
},
seriesDefaults: {
type: "column"
},
series: [{
name: "World",
data: [15.7, 16.7, 20, 23.5, 26.6]
}],
valueAxis: {
labels: {
format: "{0}%"
}
},
categoryAxis: {
categories: [2005, 2006, 2007, 2008, 2009]
},
tooltip: {
visible: true,
format: "{0}%"
}
});
}
$(document).ready(function () {
setTimeout(function () {
// Initialize the chart with a delay to make sure
// the initial animation is visible
createChart();
$($($('div#chart svg g')[0]).children('path')[0]).attr('fill', '#123');
$($($('div#chart svg g')[1]).children('path')[0]).attr('fill', '#321');
$($($('div#chart svg g')[2]).children('path')[0]).attr('fill', '#213');
$($($('div#chart svg g')[3]).children('path')[0]).attr('fill', '#312');
$($($('div#chart svg g')[4]).children('path')[0]).attr('fill', '#132');
}, 400);
});
</script>
Another way you can do it at runtime is using function which returns color.
API reference
Here is an example code:
<div id="chart"></div>
<script>
$("#chart").kendoChart({
series: [{
data: [1, 2],
color: function(point) {
if (point.value > 1) {
return "red";
}
// use the default series theme color
}
}]
});
</script>
This is not currently possible with current KendoUI version.
It's on their todo-list.
http://www.kendoui.com/forums/dataviz/chart/change-bar-column-color-for-each-point.aspx
There can be a kind of workaround, as said in the thread I've put here, it would be to describe a series for each color you need. It looks very inefficient to me, but it's currently the only way to do it. If you have only 1 chart, you could do it. Else, wait for a new version of KendoUI with this feature.
Telerik have recently released a Kendo UI Data Vis Theme Roller
http://demos.kendoui.com/themebuilder/dataviz.html

Categories