HighCharts Keep Vertical Line on Click Event - highcharts

Using this example: http://jsfiddle.net/gh/get/jquery/1.7.2/highslide-software/highcharts.com/tree/master/samples/stock/demo/candlestick-and-volume/
When you hover over points on the chart, you get a nice vertical line showing you which point you're currently on. I want to modify the click event so that the vertical line stays when I hover away after a click. Changing the line color would be ideal on click, but not necessary.
If I click another point I'd want to remove any previous lines. Any ideas on how I could accomplish this?

The above solution like I said, is really cool, but is kind of a hack (getting the path of the crosshair) into the implementation details of highcharts, and may stop working in future releases, may not be totally cross browser (esp since <IE8 do not support SVG, the adding path may still work as it should be handled by highchart's add path method, but getting the crosshair's path may not work, I may be wrong, am an SVG noob). So here I give you the alternate solution of dynamically adding plotLines. PlotLines also allow some additional features like dashStyles, label etc.
get the axis and x value of point clicked (may not exactly overlap the crosshair)
var xValue = evt.xAxis[0].value;
var xAxis = evt.xAxis[0].axis;
Or
EDIT If you want to have the plotLine at the location of the crosshair and not the click position, you can use following formula (No direct API to get this, obtained from source code hence may stop working if code changes)
var chart = this;
var index = chart.inverted ? chart.plotHeight + chart.plotTop - evt.chartY : evt.chartX - chart.plotLeft;
var xValue = chart.series[0].tooltipPoints[index].x;
Add plotline
xAxis.addPlotLine({
value: xValue,
width: 1,
color: 'red',
//dashStyle: 'dash',
id: myPlotLineId
});
You can cleanup existing plotline
$.each(xAxis.plotLinesAndBands,function(){
if(this.id===myPlotLineId)
{
this.destroy();
}
});
OR
try {
xAxis.removePlotLine(myPlotLineId);
} catch (err) {}
Putting the pieces together
var myPlotLineId="myPlotLine";
...
var chart=this;
index = chart.inverted ? chart.plotHeight + chart.plotTop - evt.chartY : evt.chartX - chart.plotLeft;
var xValue = chart.series[0].tooltipPoints[index];
// var xValue = evt.xAxis[0].value; // To use mouse position and not crosshair's position
var xAxis = evt.xAxis[0].axis;
$.each(xAxis.plotLinesAndBands,function(){
if(this.id===myPlotLineId)
{
this.destroy();
}
});
xAxis.addPlotLine({
value: xValue,
width: 1,
color: 'red',
//dashStyle: 'dash',
id: myPlotLineId
});
...
Add plot lines at click position # jsFiddle
Persist crosshair/cursor as plot lines on click # jsFiddle

You can do it in several ways
Highchart has a very cool renderer that allows you to add various graphics to the chart. One of the options is to add a path I will be illustrating the same here.
We shall reuse the path of the crosshair and add the same to the chart with some additional styles like color you mentioned. The path of the crosshair can be optained as this.tooltip.crosshairs[0].d this is in string form and can be easily converted to an array using the Array.split() function
click: function() {
this.renderer.path(this.tooltip.crosshairs[0].d.split(" ")).attr({
'stroke-width': 2,
stroke: 'red'
}).add();
}
This will accomplish adding the line. You can store the returned object into a global variable and then when you are about to add another such line, you can destroy the existing one by calling Element.destroy()
var line;
...
chart:{
events: {
click: function() {
if (line) {
line.destroy();
}
line = this.renderer.path(this.tooltip.crosshairs[0].d.split(" ")).attr({
'stroke-width': 2,
stroke: 'red'
}).add();
}
}
...
Persist tooltip / crosshair on click # jsFiddle
Assuming you don't have much meta data to be shown along with the line, this is the easiest (or the coolest :) ) approach. You can also attach meta data if you want to using the renderer's text object etc.
An alternate way could be adding vertical plotLines to the xAxis
UPDATE
Refer my other solution to this question, that would work with zoom,scroll,etc

Related

How to position highcharts tooltip above chart with outside:true

I have a system with lots of highcharts which can be positioned basically anywhere on the page.
Some are very small (e.g. 50px x 50px).
I see we can set tooltip.outside : true
tooltip: {
outside: true
}
This stops the tooltip taking over the whole chart container by breaking it out of the chart and into the window.
However this can cause overflow issues with the window when you're near the edge of the page and I personally prefer a static tooltip.
I'd like to always fix the tooltip to float above the chart, top left with a bit of padding, which for my application will almost always be visible and will avoid overflow issues, e.g.;
I've looked into setting a custom positioner, however, as the "outside" tooltip is now part of the window and not relative to the chart, the positioner sets a fixed position, which isn't suitable for my application.
I'd like the tooltip to always be above the chart regardless of mouse or scroll positions.
Of course I could add a custom element and position it above the chart myself, then applying the tooltip to that, but we have a lot of charts and this seems cumbersome.
Tooltip outside Fiddle
Suggestions?
Thanks
After a bit of poking around in the highstock Tooltip.prototype.getPosition code, it turns out what I needed was this.point.chart.pointer.getChartPosition();
tooltip: {
distance: 40,
outside: true,
positioner: function () {
var point = this;
var chart = point.chart;
var chartPosition = chart.pointer.getChartPosition();
var distance = point.distance;
//Position relative to renderTo container
return {
x: chartPosition.left - distance,
y: chartPosition.top - distance - (point.options.useHTML == true ? point.label.div.offsetHeight : point.label.height)
}
//Alternatively - Position relative to chart plot (ignoring legend)
var containerScaling = chart.containerScaling;
var scaleX = function (val) {
return (containerScaling ? val * containerScaling.scaleX : val);
};
var scaleY = function (val) {
return (containerScaling ? val * containerScaling.scaleY : val);
};
return {
x: chartPosition.left - distance + scaleX(chart.plotLeft),
y: chartPosition.top - distance + scaleY(chart.plotTop) - point.label.height
}
},
},
See working fiddle.
I find it odd that this method is attached to pointer, but it's what I was after.
One thing to note, in the fiddle I use point.label.height, if useHTML:true; use point.label.div.height.
What about using the positioner callback without setting the 'outside' option? It will set the wanted position inside the chart area.
Demo: https://jsfiddle.net/BlackLabel/gabjwd2e/
API: https://api.highcharts.com/highcharts/tooltip.positioner

Position shared tooltip above the stacked columns

When using stacked columns, I would like the tooltip to be positioned above the stacked columns. Right now, the tooltip will appear above the hovered part of the column, like this:
I would like the tooltip to always appear above the stacked columns regardless of the hovered part, like this:
(source: i.ibb.co)
I know about the positioner method, but this function doesn't seem to receive the proper parameters for me to position the tooltip above the stacked columns. In particular I don't know how to properly get the coordinates of the hovered column, all I get is the global position of the cursor.
You can use the shared parameter for a tooltip:
tooltip: {
shared: true
}
Live demo: http://jsfiddle.net/BlackLabel/us4h659d/
API Reference: https://api.highcharts.com/highcharts/tooltip.shared
So I finally managed to do it using Axis.toValue() and Axis.toPixels() values:
https://api.highcharts.com/class-reference/Highcharts.Axis
For my solution to work you need to have a way to get the total value of a stacked column. There may be a way to do it using only the Highcharts object, but I don't like messing too much with the internals of Highcharts for compatibility reasons.
Here's what the positioner method could look like:
function positioner(labelWidth, labelHeight, point)
{
// Default position, assuming mChart is the Highcharts object
var chartY = point.plotY + mChart.plotTop;
var chartX = point.plotX + mChart.plotLeft;
// Move chartY above the stacked column, assuming getTotalColumnValue() exists
var category = Math.round(mChart.xAxis[0].toValue(point.plotX, true));
if(category in mChart.xAxis[0]['categories'])
chartY = mChart.yAxis[0].toPixels(getTotalColumnValue(category), false);
// Move tooltip above the point, centered horizontally
return {
x: chartX - labelWidth / 2,
y: chartY - labelHeight - 10,
};
}

Highcharts: How to add an arrow head at the tip of a connector in a pie chart?

I would like to be able to add a dot at the tip of a connector in a pie highchart. (using Highcharts 5.0.10)
I learned that there are <marker> svg elements (not to be confused with the highcharts marker api) and these can be added either:
via css
as an attribute on the <path> element.
Although I don't see a way to actually apply them.
I tried appending a marker element during the render event in highcharts and then modifying the connector element to add the attribute:
render: function() {
var marker = '<marker xmlns="http://www.w3.org/2000/svg" id="circle-marker" refX="5" refY="5" markerUnits="strokeWidth" markerWidth="4" markerHeight="3" orient="auto" viewBox="0 0 10 10">'+
'<circle cx="5" cy="5" r="3" style="stroke: none; fill: red;"/>' +
'</marker>';
if (!$('#circle-marker').length) {
$('defs').append(marker);
}
$('.highcharts-data-label-connector').attr('marker-end', 'url(#circle-marker)');
}
Also tried creating a style that would point to that marker.
The more complex part of the issue is I want to make them dynamic (grab the color from the current point).
I might need to get my hands dirtier on SVG (I practically don't know a thing), but also think there is something I am missing from the Highcharts API.
EDIT: This is all done inside an AngularJS directive.
You can use Highcharts svg wrapper to create markers and attach them correctly on load event. The issue with the marker is - if you want them to have the same color as the connector,you need to create a separate marker for each - in SVG 1.1 markers do not inherit the color from the objects which refers to them (markers).
load: function () {
var renderer = this.renderer
this.series[0].points.forEach((point, i) => {
var marker = renderer.createElement('marker').add(renderer.defs).attr({
markerWidth: 10,
markerHeight: 10,
refX: 5,
refY: 5,
orient: 'auto',
id: `connector-marker-${i}`
})
renderer.circle(5, 5, 5).add(marker).attr({
fill: point.color
})
point.connector.attr({
'marker-start': `url(#connector-marker-${i})`
})
})
}
example: http://jsfiddle.net/39xvhwqo/

Is there a way of dynamically toggling the Highstock navigator to regain vertical space for the chart?

I'd like to be able to be able to dynamically toggle the presence of the Highstock navigator and allow the chart to expand into the vertical space it occupied.
I've tried simply toggling chart.userOptions.navigator.enabled but it has no effect.
This thread explains how to use .hide() and .show() methods to conceal the individual components of the navigator and scrollbar, but these use visibility:hidden so the space does not become available for the chart. However, using .css({display: 'none'}) works, but the series itself has no .css() method, and I've been unable to find a way of removing the series from just the navigator.
Does anyone know a method to achieve what I want?
Thanks.
In short: it's not supported to hide navigator in real time. The best way would be to destroy chart and create new one with disabled navigator.
Other solution is to use workaround provided by Sebastian Bochan. Then you will need to update manually yAxis.height, for example: http://jsfiddle.net/dJbZT/91/
$('#btn').toggle(function () {
chart.yAxis[0].defaultHeight = chart.yAxis[0].height;
chart.xAxis[0].defaultHeight = chart.xAxis[0].height;
chart.yAxis[0].update({
height: 500 - chart.plotTop - 35
}, false);
chart.xAxis[0].update({
height: 500 - chart.plotTop - 35
});
chart.scroller.xAxis.labelGroup.hide();
chart.scroller.xAxis.gridGroup.hide();
chart.scroller.series.hide();
chart.scroller.scrollbar.hide();
chart.scroller.scrollbarGroup.hide();
chart.scroller.navigatorGroup.hide();
$.each(chart.scroller.elementsToDestroy, function (i, elem) {
elem.hide();
})
}, function () {
chart.yAxis[0].update({
height: chart.yAxis[0].defaultHeight
}, false);
chart.xAxis[0].update({
height: chart.xAxis[0].defaultHeight
});
chart.scroller.xAxis.labelGroup.show();
chart.scroller.xAxis.gridGroup.show();
chart.scroller.series.show();
chart.scroller.navigatorGroup.show();
chart.scroller.scrollbar.show();
chart.scroller.scrollbarGroup.show();
$.each(chart.scroller.elementsToDestroy, function (i, elem) {
elem.show();
})
});
There's another way to do this: reduce the chart height by the navigator's height, and set chart.reflow to false to prevent the Y-axis from adapting to the new chart height (try setting it to true in the fiddle -- notice the flicker when you show / hide the navigator?).
I've added this answer to the other thread, and the demo is here: http://jsfiddle.net/dJbZT/148/ (credits to Sebastian Bochan for the original answer).
I'm not sure when highcharts added this ability via options, but this worked for me:
var chart = $('#graphContainer').highcharts();
chart.options.navigator.enabled = !chart.options.navigator.enabled;
$('#graphContainer').highcharts(chart.options);

Highcharts - programmatically draw a line or graphic between two related points

I often have charts that require a design element like a curly brace to call attention to call attention to a range or comparison in a graph, such as the y-difference in two points at the end of a graph.
My first take is that this would be a job for Highcharts Renderer API. Load the graph, and run a callback that adds an image (or line, shape, whatever) via chart.renderer.image(...) or similar.
That's the approach I have started down, but I'm just missing how to get the coordinates for chart data points within the callback. Here's a working codepen of the code below. What doesn't work is that there's no logic to give it proper placement on the canvas (suppose I want the bracket to go from the final top point to the final bottom point)
$('#container').highcharts({
data: { table: document.getElementById('datatable') },
chart: { type: 'line' },
title: { text: 'Data extracted from a HTML table in the page'
}
}, function(chart){
var img = 'http://upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Curly_bracket_right.svg/30px-Curly_bracket_right.svg.png';
// How can I populate these values?
var x = 0; // should programmatically get x-position of last point
var y = 0; // should programmatically get y-position of last point
var h = 100; // should programmatically get distance between y-position of top and bottom points
var w = 50;
chart.renderer.image( img, x, y, w, h ).add();
});
Is there a straightforward way to populate those values? Or is there a better way to do this entirely?
to get the position you want you can use few methods provided by highcharts in their API.
methods like toPixels(), toValue() will help you to alter your required position as per the chart demogrphics.
please refer their api
toPixels() : http://api.highcharts.com/highcharts#Axis.toPixels()
toValue() : http://api.highcharts.com/highcharts#Axis.toValue()
hope using this will solve your requirement of positioning

Resources