I am trying to show the total of the Y axis as a subtitle for a HighStock chart.
This is initially accomplished by summing up the point.y value of all the series, inside the "load" event like so:
chart: {
events: {
load: function () {
var chart = this,
series = chart.series,
seriesSum = 0;
series.forEach(function (series) {
series.data.forEach(function (point) {
seriesSum += point.y
})
})
this.update({
subtitle: { text: 'TOTAL: ' + seriesSum }
});
},
}
}
Now I need to update the TOTAL amount after the chart has been redrawn after changing the timeframe using either the Navigator or the Range Selector. I know there is a "redraw" event, but that will end me up in an infinite loop. Which event should I be tack onto?
You can use the redraw event, and just not redraw within a pending redraw. For example:
chart: {
events: {
redraw: function () {
this.update({
subtitle: { text: 'TOTAL: ' + seriesSum }
}, false);
}
}
}
Note the false parameter to the Chart.update function to prevent further redrawing (infinite loop).
See this JSFiddle demonstration using Highcharts and the redraw event.
You can use update method with redraw flag in redraw event, but you must set right conditions to not to fall into an infinite loop:
var redrawEnabled = true;
var chart = Highcharts.chart('container', {
chart: {
zoomType: 'x',
events: {
redraw: function() {
if (redrawEnabled) {
var sum = 0;
redrawEnabled = false;
Highcharts.each(this.series[0].points, function(point) {
if (point.isInside) {
sum += point.y;
}
});
this.update({
subtitle: {
text: sum
}
});
redrawEnabled = true;
}
}
}
},
series: [{
data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
}]
});
Live demo: http://jsfiddle.net/BlackLabel/dewumkz6/
Related
I would like to have a series of data points displayed on my chart but only a specific one that would be "displayed" and draggable. Is this possible ? I'm using Highcharts javascript
Thanks for the help
You can return false from drag and drop event callback functions to prevent dragging.
series: [{
dragDrop: {
draggableY: true
},
point: {
events: {
drag: function() {
if (this.color !== 'red') {
return false;
}
},
drop: function() {
if (this.color !== 'red') {
return false;
}
}
}
},
data: [0, 71.5, {
y: 106.4,
color: 'red'
}, ...
]
}]
Live demo: https://jsfiddle.net/BlackLabel/rx1s73va/
API Reference: https://api.highcharts.com/highcharts/series.line.point.events
I have a chart with multiple y axes. I have moved one chart to bottom using top option. When I hover on the graph moved to bottom, shared tooltip does not appear. When I hover on the space just above the bar chart. Space between the bar and 100 (in Y axis), the tool -tip does not appear. Hover on the space right or left to the bar, tool-tip does not appear.
I don't want to have the graph in its default position. It looks cleaner when I have two graphs separated. Can I make the shared tool tip work when graph is moved down ?
My code:
yAxis: [{
top: 148
},
{
top: 0
}],
tooltip: {
shared: true,
crosshairs: {
color: 'rgba(27,161,218,0.5)',
dashStyle: 'solid',
zIndex: -1
}
},
Here is the fiddle: multi-axes graph with positioning
Any input appreciated.
Thanks
Look at using synchronized charts.
http://www.highcharts.com/demo/synchronized-charts
The JSFiddle is updated to use synchronized charts.
JSFiddle
$(function() {
$('#container').bind('mousemove touchmove touchstart', function(e) {
var chart,
point,
i,
event;
for (i = 0; i < Highcharts.charts.length; i = i + 1) {
chart = Highcharts.charts[i];
event = chart.pointer.normalize(e.originalEvent); // Find coordinates within the chart
point = chart.series[0].searchPoint(event, true); // Get the hovered point
if (point) {
point.highlight(e);
}
}
});
/**
* Override the reset function, we don't need to hide the tooltips and crosshairs.
*/
Highcharts.Pointer.prototype.reset = function() {
return undefined;
};
/**
* Highlight a point by showing tooltip, setting hover state and draw crosshair
*/
Highcharts.Point.prototype.highlight = function(event) {
this.onMouseOver(); // Show the hover marker
this.series.chart.tooltip.refresh(this); // Show the tooltip
this.series.chart.xAxis[0].drawCrosshair(event, this); // Show the crosshair
};
/**
* Synchronize zooming through the setExtremes event handler.
*/
function syncExtremes(e) {
var thisChart = this.chart;
if (e.trigger !== 'syncExtremes') { // Prevent feedback loop
Highcharts.each(Highcharts.charts, function(chart) {
if (chart !== thisChart) {
if (chart.xAxis[0].setExtremes) { // It is null while updating
chart.xAxis[0].setExtremes(e.min, e.max, undefined, false, {
trigger: 'syncExtremes'
});
}
}
});
}
}
var dataset = [{
"name": "Series 1",
"type": "column",
"data": [29.9, 71.5, 106.4]
}, {
"name": "Series 2",
"type": "line",
"data": [216.4, 194.1, 95.6]
}];
for (var i = 0; i < dataset.length; i++) {
var dataitem = dataset[i];
$("<div class=\"chart\">")
.appendTo('#container').highcharts({
title: {
text: dataitem.name
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar']
},
tooltip: {
crosshairs: {
color: 'rgba(27,161,218,0.5)',
dashStyle: 'solid',
zIndex: -1
}
},
series: [{
data: dataitem.data,
name: dataitem.name,
type: dataitem.type
}]
});
};
});
I am creating my high chart object from a json_encoded array passed in from a data id on the containing div.
$('div.filter_pie_chart').each(function( index ) {
$(this).highcharts($(this).data("params"));
});
I would like to listen for click events of a pie chart using the following notation. Is this possible?
$(document).on('click','.high_chart_point_object', function (event) {
return false;
});
I think this documentation link is what you need.
Here is a JsFiddle.
$(function () {
// create the chart
var mychart = $('#container').highcharts({
chart: {
events: {
click: function(event) {
alert ('x: '+ event.xAxis[0].value +', y: '+
event.yAxis[0].value);
}
}
},
xAxis: {
},
series: [{
data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
}]
});
});
And I think these lines should work too (not tested, but it is similar to your notation) :
$('.high_chart_point_object').highcharts({
chart: {
events: {
click: function(event) {
return false;
}
}
}
});
If you want to change options dynamically, here is the solution :
var options = mychart.options;
options.chart.events.click = function () {alert('do what you want')};
mychart = new Highcharts.Chart(options);
Here is another JsFiddle.
Yes, this should possible in three ways:
one is to use plotOpttions.pie.point.events.click ant there call function you need to. Reference.
another one is to Element.on() function from Highcharts after digging for DOM elements - it's the same as using $().on(), reference.
if you really need that $().on(), you can use is on point.element, sample for you: http://jsfiddle.net/UWZAe/
And code for third case:
chart: {
events: {
load: function() {
var chart = this,
pie = chart.series[0],
pieData = pie.data,
dLen = pieData.length;
for(var i = 0; i < dLen; i++){
var slice = pieData[i];
console.log(slice, $(slice.graphic));
}
}
}
}
After any sort of zoom (mouse drag, range selector, date input) the datetime returned from the point click event is usually incorrect. I've not yet found this problem when using an area chart, have found it using both bar and column chart.
To recreate: run the fiddle, zoom using the mouse across a few of the columns, click a datapoint. The alert will show the datetime returned. Notice it's different from the tooltip (which is correct).Usually fails after first click, even for the same datapoint.
BTW useUTC setting doesn't matter.
Fiddle: http://jsfiddle.net/jrEDT/
Code for completeness:
$(function() {
var seriesOptions = [],
yAxisOptions = [],
seriesCounter = 0,
names = ['MSFT'],
colors = Highcharts.getOptions().colors;
$.each(names, function(i, name) {
$.getJSON('http://www.highcharts.com/samples/data/jsonp.php?filename='+ name.toLowerCase() +'-c.json&callback=?', function(data) {
seriesOptions[i] = {
name: name,
data: data,
type: 'column'
};
// As we're loading the data asynchronously, we don't know what order it will arrive. So
// we keep a counter and create the chart when all the data is loaded.
seriesCounter++;
if (seriesCounter == names.length) {
createChart();
}
});
});
// create the chart when all data is loaded
function createChart() {
Highcharts.setOptions({
global: {
useUTC: false // datetime reflects time on db (ie, local) rather than GMT
}
});
chart = new Highcharts.StockChart({
chart: {
renderTo: 'container',
zoomType: 'x'
},
exporting: {
enabled: false
},
rangeSelector: {
selected: 4
},
yAxis: {
plotLines: [{
value: 0,
width: 2,
color: 'silver'
}],
offset: 25
},
plotOptions: {
series: {
cursor: 'pointer',
allowPointSelect: true,
point: {
events: {
click: function() {
var series = this.series.name;
var utc = this.x;
var d = Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x);
alert(d);
}
}
}
}
},
tooltip: {
formatter:function(a,b,c){
var d = Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x);
return d;
},
enable:true
},
series: seriesOptions
});
}
});
Thanks!
Have you tried to disable datagrouping http://api.highcharts.com/highstock#plotOptions.series.dataGrouping ?
By default Highcharts allows you to click on a data series set to hide it / unhide it.
A much more useful approach would be to do the reverse logic - ie to show only the selected series and hide/unhide the non-selected.
Looking at the example here ( http://jsfiddle.net/t2MxW/14/ ), it is clear one can 'intercept' the the 'legendItemClick' event, I am just not sure how to implement the require logic
One can replace the below script to obtain 3 data sets.
DESIRED SCENARIO: to be able to click on 'apples' and show/hide 'pears' and 'oranges' for example.
================= PASTE START =======================================
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container'
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
},
plotOptions: {
series: {
events: {
legendItemClick: function(event) {
var visibility = this.visible ? 'visible' : 'hidden';
if (!confirm('The series is currently '+
visibility +'. Do you want to change that?')) {
return false;
}
}
}
}
},
series:[{name: 'apples',
data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]},
{name:'pears',
data: [19.9, 81.5, 96.4, 119.2, 124.0, 166.0, 155.6, 138.5, 116.4, 144.1, 95.6, 54.4]},
{name:'oranges',
data: [119.9, 181.5, 46.4, 219.2, 24.0, 66.0, 255.6, 238.5, 16.4, 44.1, 95.6, 54.4]}
]
});
Every event in HighCharts contains this value which contains current element (series in this case). You can select all series using this.chart.series and handle them in any way you want. Try this function.
legendItemClick: function(event) {
if (!this.visible)
return false;
var seriesIndex = this.index;
var series = this.chart.series;
for (var i = 0; i < series.length; i++)
{
if (series[i].index != seriesIndex)
{
series[i].visible ?
series[i].hide() :
series[i].show();
}
}
return false;
}
fiddle: https://jsfiddle.net/t2MxW/21971/
I wanted to do something similar... I wanted to have it so that if you control-click (or cmd-click) a legend item, it would hide ALL OTHER items. (But leave normal clicks as their default behavior).
plotOptions: {
series: {
events: {
legendItemClick: function(e) {
// Upon cmd-click of a legend item, rather than toggling visibility, we want to hide all other items.
var hideAllOthers = e.browserEvent.metaKey;
if (hideAllOthers) {
var seriesIndex = this.index;
var series = this.chart.series;
for (var i = 0; i < series.length; i++) {
// rather than calling 'show()' and 'hide()' on the series', we use setVisible and then
// call chart.redraw --- this is significantly faster since it involves fewer chart redraws
if (series[i].index === seriesIndex) {
if (!series[i].visible) series[i].setVisible(true, false);
} else {
if (series[i].visible) series[i].setVisible(false, false);
}
}
this.chart.redraw();
return false;
}
}
}
}
}
Just sharing cause #igor's answer doesn't work on me, so I made adjustments, here's the fiddle (forked from the #igor's answer)
http://jsfiddle.net/index/Nkeep/
Forked index's answer and added functionality to have individual toggles for each series. http://jsfiddle.net/c4Zd8/1/
$.each(allSeries, function(index, series) {
if (selected == index) {
if (series.visible == true) {
series.hide();
}
else {
series.show();
}
}
});
If you want to keep the normal functionality but also be able to show/hide all then create a button or a link for a show_all()/hide_all() javascript method.
this method initializes a counter and starts the showing/hiding:
counter = 0;
setTimeout(process_hide, 1);
function process_hide()
{
your_chart.series[counter].hide();
counter+=1;
if (counter < read_chart.series.length) {
setTimeout(process_hide, 1);
}
}
The reason you do this instead of just doing $.each(your_chart, function(i,v){v.hide()}) is that it locks up the UI - using the timeout you'll actually see the series being hidden one by one - and if you want to modify something else - like a process meter, it'll actually work.
Forked because I wanted behavior to change. Originally when a user selected two series and then tried to unselect one ... the whole series would swap visibility. I check to see if all other series are "all else visibile" or "all else not visible" first before swapping the visibility for the series.
http://jsfiddle.net/nd0dcdmz/3/
legendItemClick: function(e) {
var seriesIndex = this.index;
var series = this.chart.series;
// test for if all other series besides one selected are visible or not visible
var allElseVisible = series.every(
s => (s.index == seriesIndex ? true : s.visible),
);
var allElseNotVisible = series.every(
s => (s.index == seriesIndex ? true : !s.visible),
);
// if everything else is deselected or selected ... swap visibility
// else swap the visibility of selected object.
if (allElseVisible || allElseNotVisible) {
series.map(s => {
if (s.index != seriesIndex) {
s.visible ? s.hide() : s.show();
}
});
} else {
return true;
}
return false; // overrides default behavior
},