So I have a highchart where I would like to display datalabels only on the first point of each stack (i.e. where the 0 point is)
my config looks like:
{
chart: {type: 'bar'},
plotOptions: {
series: {
stacking: 'percent',
dataLabels: {
enabled: true,
allowOverlap: true,
formatter: function () {
if (pointIsFirstInStack(this)) {
return labelFunction(this);
}
return '';
}
}
}
},
series: [{
name: 'Available',
data: [
{x: 'Event1', y: 18},
{x: 'Event2', y: 20}
]
}, {
name: 'Purchased',
data: [
{x: 'Event1', y: 23},
{x: 'Event2', y: 40}
]
}]
}
But I'm having a hard time implementing the pointIsFirstInStack function, any ideas?
regards,
Einar
JSFiddle to illustrate my chart: http://jsfiddle.net/qam37suh/
edit: added series and stacking to the config example
edit2: added JSFiddle
You can check the series index.
If you are always going to have two series, you can check it like this:
formatter: function(){
return this.series.index == 1 ? this.x : null;
}
Example:
http://jsfiddle.net/jlbriggs/jL6zrt7d/
Highcharts stacks the series in reverse by default, so index 0 is stacked on the top.
If you are going to have a dynamic number of series, you can add a check for the number of series first, so that you are still checking for the last series:
formatter: function(){
var len = this.series.chart.series.length -1;
return this.series.index == len ? this.x : null;
}
Example:
http://jsfiddle.net/jlbriggs/jL6zrt7d/1/
( You could also set reversedStacks: false on your yAxis, and instead just check for index == 0
Example:
http://jsfiddle.net/jlbriggs/jL6zrt7d/3/
)
Or, if your goal is to put the categories inside the series instead of outside, you could just move them:
xAxis: {
categories: ['Event1', 'Event2'],
labels: {
x: 60,
style: {
fontWeight: 'bold',
color: 'rgba(255,255,255,0.75)'
}
}
}
Example:
http://jsfiddle.net/jlbriggs/jL6zrt7d/2/
Related
I am trying to make a chart which has years along the x-axis and dollar amounts along the y-axis. I finally got close to what I'm looking for, but I found that because the x coordinates are numbers, ChartJS is putting commas in them which looks really strange for years.
After some digging, I used the callbacks. options.plugin.tooltip.callbacks.label worked to let me remove commas in the tooltips, but when I use options.scales.x[0].ticks.callback to try to fix the labels on the bottom, not only does it not work, but I don't see the console.log statement in their ever being printed so it seems it's not even calling the callback. I've tried several variations of how to do the callback based on what I found online and on Stack Overflow which I think correspond to the different ways ChartJS did this in different versions. (I'm on version 3.5.1.)
Then, I realized that... none of the options under options.scales appear to have any effect. I change the min, the title, the tick settings (color to red, callback, etc.) and it has no effect. (This also explains why I was having trouble when using the line chart and had to switch to scatter; apparently type: 'linear' wasn't being picked up nor did it do anything different when I set it to type: 'date' or whatever the exact working was for that.)
Meanwhile, the other options like options.showLine or options.elements do have an effect and I'm seeing the chart and not getting any errors in the console. So, it is picking up the options, just ignoring everything I have in options.scales.
Here is the relevant code:
// Sample data added to make this example self-contained
// This is my internal data format
let data = {
"Series1": [ {x: 2001, y: 100 }, {x: 2002, y: 110 }, {x: 2003, y: 107 }, ],
"Series2": [ {x: 2001, y: 107 }, {x: 2002, y: 102 }, {x: 2004, y: 95 }, ],
}
// Define data //////////////////////////////////////////////////////
// I convert data to format ChartJS wants and add a few options
let datasets = [];
for(let label in data) {
let c = colorIterator.next().value
datasets.push({
label: label,
data: data[label],
backgroundColor: c,
borderColor: c,
});
}
// Define options //////////////////////////////////////////////////////
let chartConfig = {
type: 'scatter',
data: { datasets: datasets, },
options: {
title: { display: false },
indexAxis: 'x', responsive: true, maintainAspectRatio: false,
showLine: true,
elements: {
line: { display: true, tension: 0, borderWidth: 1, fill: false, },
point: { radius: 3 }
},
interaction: { mode: 'x', },
scales: {
x: [{
type: 'linear',
min: 1995, max: (new Date()).getFullYear()+1, stepSize: 1,
title: { display: true, text: 'Year' },
ticks: {
display: true,
major: { enabled: true },
color: 'red',
callback: function(value, index, ticks) {
console.log(value);
return Chart.Ticks.formatters.numeric.apply(this, [value, index, ticks])
.replace(",","");
}
}
}],
y: [{
title: { display: true, text: '$' },
ticks: {
display: true,
color: 'red',
},
}],
},
plugins: {
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if(label) {
let x = context.label.replace(",","");
let y = context.formattedValue;
return 'Year ' + x + ' "' + label + '": $' + y;
} else { return 'x'; }
},
},
},
},
}
};
// MAKE CHART //////////////////////////////////////////////////////
let mainChart = new Chart(document.getElementById(c.id), chartConfig);
As described in the docs the scales are not arrays. All the scales are objects in the scale object.
So you will need to change your code to this:
options: {
scales: {
x: {
// x options
},
y: {
// y options
},
}
}
I created a bubble chart using ChartJs and populating data dynamically using Json.
See the code below.
for (var i = 0; i < response.data.length; i++) {
var point_data = [];
point_data.push({
x: response.data[i]['return_tickets'].toString(),
y: Math.round(response.data[i]['return_percentage']).toString(),
r: Math.round((response.data[i]['return_percentage'])).toString()
});
data.push({ label: response.data[i]['staff_name'], data: point_data, backgroundColor: getRandomColor(), hoverRadius:4 });
}
new Chart(document.getElementById("bubbleChart"), {
type: 'bubble',
data: {
datasets: data
},
options: {
title: {
display: true,
text: ''
}, scales: {
yAxes: [{
scaleLabel: {
display: true,
labelString: "Return Tickets %"
}
}],
xAxes: [{
scaleLabel: {
display: true,
labelString: "Return Tickets"
}
}]
}
}
});
It generates the desired chart as below
The problem is when I hover over any bubble the size of the bubble increases exponentially.
How to keep the size same ?
I'm setting the hoverRadius property of the dataset but it does nothing for me.
Problem is with your this line of code:
{ label: response.data[i]['staff_name'], data: point_data, backgroundColor: getRandomColor(), hoverRadius:4 }
This is not a valid JSON. Values must be either strings or arrays. Most probably issue is at label: response.data[i]['staff_name'] or in point_data (I can see you are making x, y and r values .toString() that maybe not required). Check it again. Create a valid JSON and then try by setting hoverRadius: 0, it will work.
Setting hoverRadius: 0 working fine for me. Bubble size will not change on mouse over if you set hoverRadius: 0.
Below is working example:
var chart = new Chart(ctx, {
type: 'bubble',
data: {
datasets: [{
label: 'Bubble',
data: [{
x: 5,
y: 55,
r: 27.5
}],
backgroundColor: 'rgba(0, 119, 290, 0.6)',
hoverRadius: 0
}]
},
options: {
tooltips: {
callbacks: {
label: function(t, d) {
return d.datasets[t.datasetIndex].label +
': (Day:' + t.xLabel + ', Total:' + t.yLabel + ')';
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<canvas id="ctx"></canvas>
Checkout official documentation for more info : https://www.chartjs.org/docs/latest/charts/bubble.html#dataset-properties
I have already faced the same issue also fixed it by typecasting for every x,y & z. I just convert it to float
'x' => (float) $x_axis_value,
'y' => (float) $y_axis_value,
'r' => (float) $radious_value,
I am trying to customize stacked column chart like this
Here i did all the remaining things but i Don't know how to give that bar lines above every column........I need that bar lines in both positive and negative axis
My code
$(document).ready(function () {
$('#div1').highcharts({
chart: { type: 'column', backgroundColor: 'transparent' },
title: { text: null },
subtitle: { text: null },
credits: {
enabled: false
},
xAxis: {
categories: categories,
labels: {
rotation: 0,
style: {
fontWeight: 'normal',
fontSize: '0.9vw',
fontFamily: 'Verdana, sans-serif',
color: "black"
}
}
},
yAxis: {
title: {
enabled: false
},
lineWidth: 0,
gridLineWidth: 1,
labels: {
enabled: true
},
// gridLineColor: 'transparent',
plotLines: [{
color: '#ddd',
width: 1,
value: 0
}],
},
plotOptions: {
series: {
stacking: 'normal'
}
},
series:seriesforSeniorUPT
});
});
});
Link
Fiddle
To elaborate on Sebastian Bochan's helpful comment, here's an updated version of your fiddle with two "dummy" series to serve as the patterned background: https://jsfiddle.net/brightmatrix/hc8rLy18/2/
A few items to note:
There are two "dummy" series: one for the positive numbers and one for the negative numbers.
Both series have showInLegend and enableMouseTracking set to false so the user cannot interact with them.
Both series have stacking set to false so they will not be part of the "real" data you want to show.
Both series have zIndex set to 0. I explain why below the code block.
The code for the "dummy" series is as follows.
// background for positive values
obj = {};
obj["name"] = 'patternFill';
obj["data"] = [120, 120];
obj["color"] = 'url(#highcharts-default-pattern-3)';
obj["grouping"] = false;
obj["zIndex"] = 0;
obj["enableMouseTracking"] = false;
obj["stacking"] = false;
obj["showInLegend"] = false;
seriesforSeniorUPT.push(obj);
// background for negative values
obj = {};
obj["name"] = 'patternFill';
obj["data"] = [-80, -80];
obj["color"] = 'url(#highcharts-default-pattern-3)';
obj["grouping"] = false;
obj["zIndex"] = 0;
obj["enableMouseTracking"] = false;
obj["stacking"] = false;
obj["showInLegend"] = false;
seriesforSeniorUPT.push(obj);
For the three "real" data series, I set zIndex to 10 to they will appear over the "dummy" series we're using for our patterend backgrounds.
For all of the series, I set grouping to false so they will appear one atop the other, not next to one another.
Here's a screenshot of the output. I hope this is helpful!
Need to draw vertical lines from a desired point rather than starting from 0.
plotLines: [{
color: '#FF0000',
width: 1,
value: 4
}, {
color: '#FF0000',
width: 1,
value: 7
}],
Here is the fiddler link: http://jsfiddle.net/bpswt3tr/4/
My requirement is to draw first vertical line from when y value is 110.2 and 2nd line from when y value is 135.6 instead of starting from zero. i.e above the plot line only. Please suggest how can I achieve this? Thanks.
Considering the documentation it is unlikely that HighCharts supports this by default, as you are only allowed to associate a value of the current axis with the line.
You might need a preprocessing step that inverts you function to get the appropriate X values. Something like:
invert(data, Y) -> list of X values with data[X] = Y
You can do this on the chart.events.load call. If you know these are the points you want to add marker elements to then it is fairly straightforward. You first get the current max label value for the yAxis. Then you add a series to the chart with the starting point being your series' value and the second point being the max viewable yAxis value. Then do the same for the second point you want to add a bar to. Then, you need to re-set the yAxis max value to the initial state because highcharts will try to increase the scale to accommodate the new points.
chart: {
events: {
load: function () {
var yMAx = this.yAxis[0].max;
console.log(yMAx);
this.addSeries({
data: [{
x: 4,
y: 110.2,
marker: {
symbol: 'triangle'
}
}, {
x: 4,
y: yMAx,
marker: {
symbol: 'triangle-down'
}
}, ],
showInLegend: false,
color: 'red',
marker: {
enabled: true
}
});
this.addSeries({
data: [{
x: 7,
y: 135.6,
marker: {
symbol: 'triangle'
}
}, {
x: 7,
y: yMAx,
marker: {
symbol: 'triangle-down'
}
}, ],
showInLegend: false,
color: 'red',
marker: {
enabled: true
}
});
this.yAxis[0].update({
max: yMAx
});
}
}
}
Sample demo.
I am using n3-charts which uses the line-chart.min.js and d3.v2.js plugin.I wanted to hide the x axis 0th tick value and last child tick value. How to do in the following image.
Kindly, someone help me to resolve this issue, I don't want the APR11 and the last tick value which will change dynamically.
app.directive('barChart', [
function () {
return {
restrict: 'EA',
template: '<linechart data="data" options="options" width="550" height="291"></linechart>',
scope: {
range: '=',
},
link: function(scope, elements, attrs){
scope.$watch("range", function () {
var values = scope.range;
scope.data = values;
scope.options = {
yaxis : {name: "Calories",labelalign:"-135"},
stacks: [{axis: "y", series: ["firstVal", "secondVal", 'thirdVal']}],
fonts: {family: 'serif', size: '14px'},
axes: {
x: {key: 'x', type: 'date',labelFunction: function(x) { return d3.time.format('%b %d')(new Date(x));}},
y: {key :'y',type: 'linear',min:0}
},
transition: {ease: 'elastic', duration: 1000, delay: 100},
series: [
{id: 'secondVal', y: 'secondVal', axis: 'y', color: "#b3d660", type: 'column', drawDots: true, dotSize: 4, label: 'Labe1'},
{id: 'firstVal', y: 'firstVal', axis: 'y', color: "#ff8669", thickness: '2px', type: 'column', label: 'Label2'},
{id: 'thirdVal', y: 'thirdVal', axis: 'y', color: "#52c4dc", type: 'column', dotSize: 2, label: 'Label3'}
],
lineMode: 'linear',
tension: 0.2,
tooltip: {
mode: 'scrubber', formatter: function (x, y, series) {
return series.label + ', ' + Math.round(y);
}
},
drawLegend: true,
drawDots: true,
columnsHGap: 5
}
});
}
};}]);
For n3-charts v2, you can define a tickFormat function for each axis.
add a function called tickFormat to scope.options.axes.x that goes something like this:
tickFormat: function(value, index){
var firstOrLast = (index === 0) || (index === scope.values.length-1);
return firstOrLast ? "" : value;
}
Not sure about n3-charts v1, but I see that you already have a function called labelFunction in scope.options.axes.x. You could do something similar to the above code, but you would just check if your parameter 'x' matches your first or last value in scope.values.