I am using latest Chart.bundle.js to create multiple stacked Bar charts on same page. For example, I have 3 charts with 2 datasets. Charts are correct, but tooltips on each of the charts are always the same and showing wrong values. screenshot link. All the values are zero, which is obviously wrong.
Tooltips mode is index, canvas for each chart has different id, dataset variables are all different.
Trying to post my code on jsfiddle, I was able to find the solution to this very odd problem. I had
var barCharData = {
labels: ["2", "3", "4"],
datasets: []
};
, so there is no data in any of my charts at the beginning (before getting data from database). For every chart I have I did something like this:
for(var i=1; i <= number_of_charts; i++){
bar_char_data_array.push(barChartData);
var myBar = new Chart(ctx, {
type: 'bar',
data: bar_char_data_array[i-1],
options: {
tooltips: {
mode: 'label',
intersect: true
}
}
});
}
It turned out that javascript considers every element of array "bar_char_data_array" to be the same object and changing data for one of the charts resulted in changing data for all the rest. I still don't know why all the charts were correct until hovered to see tooltip. Nevertheless, solution for my problem was to remove barChartData variable.
for(var i=1; i <= number_of_charts; i++){
bar_char_data_array.push({
labels: ["2", "3", "4"],
datasets: []
});
var myBar = new Chart(ctx, {
type: 'bar',
data: bar_char_data_array[i-1],
options: {
tooltips: {
mode: 'label',
intersect: true
}
}
});
}
My solution was somewhat similar to the #queen-juliet's answer.
I had a shared config variable with the data key being added and updated within a loop. Even though the multiple chart.js objects I had on the page were showing the correct data, the tooltips were shown on one (random) chart only. The solution was to declare and fill the whole config inside my loop instead.
Related
I'm currently trying to write a short program that captures data on mouse movements, scrolling and keyboard clicks from my laptop. This data is then saved in a firebase real time database and deployed to a simple webpage.
I'm using chart.js to plot this data on the page, but when I plot the data, the first adn last points get connected together (see image). I'm having to use the scatter plot as opposed to the line plot as I want the values to be distributed relatively along the x axis. The line chart.js plot places them with equal spacing (which looks weird for a time series bit of data!).
As this is a time series data set this is clearly quite annoying! Anyone have any ideas how I can stop this from happening??
Any help much appreciated!
//This code is taken from a larger script, I'm fairly sure these are all the relevant parts
//This is a js file which manages 3 chart.js charts on a webpage.
//This function generates the chart template
function chartTemplateCreation(InputLabel)
{
const chartTemplate = {
type: 'scatter',
data: {
datasets: [{
label: InputLabel,
data: [],
//backgroundColor: 'rgba(255, 205, 210, 0.5)'
}]
},
options: {
legend: {
display: false
},
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [{
ticks: {
suggestedMin: 7,
suggestedMax: 22
}
}]
}
}
}
return chartTemplate
}
//These values create links to the html positioning of the charts
const blankMouseChart = document.getElementById('mouse-chart').getContext('2d')
const blankKeysChart = document.getElementById('keys-chart').getContext('2d')
const blankScrollChart = document.getElementById('scroll-chart').getContext('2d')
//Charts are created
const mouseChart = new Chart(blankMouseChart,chartTemplateCreation('mouse'))
const keysChart = new Chart(blankKeysChart, chartTemplateCreation('keys'))
const scrollChart = new Chart(blankScrollChart, chartTemplateCreation('scroll'))
//This function is used to add to new data to the chart
function addDataScatter(chart, time, value)
{
//chart.data.labels.push(label);
chart.data.datasets.forEach((dataset) =>
{
dataset.data.push({x:time,y:value})
})
chart.update()
}
//collect the firebase reference
const dataRefTenMin = firebase.database().ref(date + "/10min_culmalative/")
//initially add all the historic values from today
dataRefTenMin.once("value").then(function(snapshot)
{
snapshot.forEach(function(childSnapshot)
{
var childData = childSnapshot.val()
var decTime = childData.decTime
addDataScatter(mouseChart, decTime, childData.mouse)
addDataScatter(scrollChart, decTime, childData.scroll)
addDataScatter(keysChart, decTime, childData.keys)
})
})
//then live update the graph by adding data points every time a child is added to the firebase realtime database
dataRefTenMin.on("child_added", function(data)
{
var newData = data.val()
var decTime = newData.decTime
addDataScatter(mouseChart,newData.decTime, newData.mouse)
addDataScatter(scrollChart,newData.decTime, newData.scroll)
addDataScatter(keysChart,newData.decTime, newData.keys)
})
Ah! Figured out the answer by mistake! Accidently commented out a section of code and it now works. All you have to do is remove the section which was meant to add the historic data. As it turns out, both:
dataRefTenMin.once("value").then(function(snapshot)
{
snapshot.forEach(function(childSnapshot)
{
var childData = childSnapshot.val()
var decTime = childData.decTime
addDataScatter(mouseChart, decTime, childData.mouse)
addDataScatter(scrollChart, decTime, childData.scroll)
addDataScatter(keysChart, decTime, childData.keys)
})
})
and
dataRefTenMin.on("child_added", function(data)
{
var newData = data.val()
var decTime = newData.decTime
addDataScatter(mouseChart,newData.decTime, newData.mouse)
addDataScatter(scrollChart,newData.decTime, newData.scroll)
addDataScatter(keysChart,newData.decTime, newData.keys)
})
add historic data to the chart. Hence the error comes as they plot the data over each other (I think).
If any one else has a similar problem, remove the first one of the two portions of code above! Then the chart will both plot the historic data and update live.
Is there a way to set a different color to a datapoint in a Line Chart if its above a certain value?
I found this example for dxChart - https://stackoverflow.com/a/24928967/949195 - and now looking for something similar for ChartJS
In updating to version 2.2.2 of ChartJS, I found that the accepted answer no longer works. The datasets will take an array holding styling information for the properties.
In this case:
var pointBackgroundColors = [];
var myChart = new Chart($('#myChart').get(0).getContext('2d'), {
type: 'line',
data: {
datasets: [
{
data: dataPoints,
pointBackgroundColor: pointBackgroundColors
}
]
}
});
for (i = 0; i < myChart.data.datasets[0].data.length; i++) {
if (myChart.data.datasets[0].data[i] > 100) {
pointBackgroundColors.push("#90cd8a");
} else {
pointBackgroundColors.push("#f58368");
}
}
myChart.update();
I found this looking through the samples for ChartJS, specifically this one: "Different Point Sizes Example"
With recent versions of chart.js I would recommend doing this with scriptable options.
Scriptable options give you an easy way to vary the style of a dataset property (e.g. line point colour) dynamically according to some function you provide. Your function is passed a 'context' object that tells it the index and value of the point etc. (see below).
Most chart properties can be scripted; the dataset properties for each chart type tell you the exact list (e.g. see here for line chart).
Here is how you might use scriptable options on a line chart (based on the example in the docs). On this chart negative data points are shown in red, and positive ones in alternating blue/green:
window.myChart = Chart.Line(ctx, {
data: {
labels: x_data,
datasets: [
{
data: y_data,
label: "Test Data",
borderColor: "#3e95cd",
fill: false,
pointBackgroundColor: function(context) {
var index = context.dataIndex;
var value = context.dataset.data[index];
return value < 0 ? 'red' : // draw negative values in red
index % 2 ? 'blue' : // else, alternate values in blue and green
'green';
}
}
],
}
});
The context object passed to your function can have the following properties. Some of these won't be present for certain types of entity, so test before use.
chart: the associated chart
dataIndex: index of the current data
dataset: dataset at index datasetIndex
datasetIndex: index of the
current dataset
hover: true if hovered
Here's what worked for me (v 2.7.0), first I had to set pointBackgroundColor and pointBorderColor in the dataset to an array (you can fill this array with colours in the first place if you want):
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
data: dataPoints,
pointBackgroundColor: [],
pointBorderColor: [],
}
]
}
});
Then you can monkey with the colours of the points directly:
myChart.data.datasets[0].pointBackgroundColor[4] = "#cc00cc";
myChart.data.datasets[0].pointBorderColor[4] = "#cc0000";
myChart.update();
Some other properties to play with to distinguish a point: pointStrokeColor (it apparently exists but I can't seem to get it to work), pointRadius & pointHoverRadius (integers), pointStyle ('triangle', 'rect', 'rectRot', 'cross', 'crossRot', 'star', 'line', and 'dash'), though I can't seem to figure out the defaults for pointRadius and pointStyle.
For chartjs 2.0 see this following answer.
Original answer below.
Good question regarding ChartJS. I've been wanting to do a similar thing. i.e dynamically change the point colour to a different colour. Have you tried this below. I just tried it and it worked for me.
Try this:
myLineChart.datasets[0].points[4].fillColor = "rgba(000,111,111,55)" ;
Or Try this:
myLineChart.datasets[0].points[4].fillColor = "#FF0000";
Or even this:
myLineChart.datasets[0].points[4].fillColor = "lightgreen";
Then do this:
myLineChart.update();
I guess you could have something like;
if (myLineChart.datasets[0].points[4].value > 100) {
myLineChart.datasets[0].points[4].fillColor = "lightgreen";
myLineChart.update();
}
Give it a try anyway.
Just adding what worked for me in the new 2.0 version.
Instead of:
myLineChart.datasets[0].points[4].fillColor = "lightgreen";
I had to use:
myChart.config.data.datasets[0].backgroundColor[4] = "lightgreen";
Not sure if that's because of a change in 2.0 or because I'm using a bar chart and not a line chart.
If you initialize the myChart in this manner,
var myChart = new Chart(ctx, {
type: 'line',
data: {
you have to change line color by this code
myChart.data.datasets[0].backgroundColor[0] ="#87CEFA";
If you initialize the myChart in this manner,
myBar = new Chart(ctx).Line(barChartData, {
you have to change line color by this code
myLineChart.datasets[0].points[4].fillColor = "#FF0000";
I was not able to find an answer to the following what I think is best described by example:
I change one single value in radar-chart-data from 6 to 10 while the other ones stay the same. If I now trigger an update with non-zero animation-time it will repaint the chart while starting from zero for each value. I would prefer the animation to just animate the changing, i.e. a movement from 6 to 10 for the property in question. Is that possible?
Below is an example showing the chart updating without redrawing. Run the snippet and press the 'Update' button to increase data point b by 1 each time.
let myChart = new Chart(document.getElementById('chart'), {
type: 'radar',
data: {
labels: ['a', 'b', 'c', 'd', 'e'],
datasets: [{
label: 'series1',
data: [0, 2, 7, 10, 3]
}]
},
options: {
maintainAspectRatio: false
}
});
document.getElementById('update').addEventListener('click', function() {
myChart.data.datasets[0].data[1] += 1;
myChart.update();
});
<button id="update">Update!</button>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<canvas id="chart"></canvas>
Yes. It is possible. It depends on how you update the chart option. See chartjs documentations
To update the options, mutating the options property in place or passing in a new options object are supported.
If the options are mutated in place, other option properties would be preserved, including those calculated by Chart.js.
If created as a new object, it would be like creating a new chart with the options - old options would be discarded.
I'm trying to draw a stacked (area) line chart using C3.
My code, as it stands, allows me to create a line chart without stacking :
var chart = c3.generate({
data: {
x: 'x',
url: 'GeneratedData.csv',
type: 'area',
/* groups: [ ['data1', 'data2'] ] */
},
axis : {
x : {
type : 'timeseries',
tick : {
format : "%y-%m-%d"
}
}
}
});
My problem is that the data is generated in such a way that I do not know the name of the columns in advance, so I cannot set their type or group them
(hence the comments around groups: [ ['data1', 'data2'] ])
My CSV looks something like this :
x,LT62Ag,5NwafDw,Pac0dA
2017-01-22,85797,145417,626803
2017-01-23,71837,105246,440776
2017-01-24,77650,108834,442359
...
2017-03-31,87359,102618,467113
How should I proceed to create the groups from the dynamic data to stack the charts ?
You could try adding this to your chart declaration, it'll pull out the names of the data series (apart from x) and turn them into one big group:
onrendered: function () {
var seriesNames = this.data.targets.map (function (d) {
return d.id;
}).filter (function (sname) {
return sname !== "x";
});
this.api.groups ([seriesNames]);
},
Ideally it should be done with the 'oninit' declaration rather than the groups reset on every rendering, but there seems to be some sort of bug that makes the bars go 1 pixel wide when you do that...
I guess a flag that decides whether the groups have already been set could be employed though...
https://jsfiddle.net/1bb60dd9/
I'm trying to create a horizontal stacked bar chart with labels on the bars themselves (currently using dataLabels) as well as labels just above the bars.
Here is a JSFiddle I've created that somewhat resembles what I am trying to accomplish. In this example I've placed the labels using plotLine labels, but I'm having a difficult time figuring out how to place the bars correctly in my real use case as I get the data for the bars through Ajax calls.
JSFiddle
I've experimented with stackLabels, plotLine labels as well as plotBand labels but to no avail. How can I place labels above the bars correctly every time?
I would use plotBands instead of plotLines - you can better control the alignment within the band, which can be set to cover the same portion of the graph as the bar itself.
An example.
Using your series data:
var series = [{
name: 'John',
data: [5]
},
{
name: 'Jane',
data: [2]
}, {
name: 'Joe',
data: [3]
}]
We can loop through and build a plotBands array from the same data:
var bands = [],
prevData = 0;
$.each(series, function(i,ser) {
var point = ser.data[0],
name = ser.name;
bands.push({
from: prevData,
to: (prevData + point),
color: 'rgba(255,255,255,0)',
label: {
text: name,
align: 'center'
}
});
prevData = (prevData+point);
});
To make this match the order of the series as plotted, we need to set reversedStacks: false on the yAxis, as by default, Highcharts reverses the order of the series. Then we populate the plotBands with our generated array:
yAxis: {
reversedStacks: false,
plotBands: bands
}
In this way you can make use of the full feature set of the plotBands label properties.
Updated fiddle:
http://jsfiddle.net/jlbriggs/xwzfjbhe/4/
And this adjusts to fit however many data points you have, provided you keep the same format (series.name, series.data[0]):
http://jsfiddle.net/jlbriggs/xwzfjbhe/5/
There are a number of other ways you can go about this as well, including:
The labels property: http://api.highcharts.com/highcharts/labels
The renderer function: http://api.highcharts.com/highcharts/Renderer