My Highcharts solutions give the user the option to control which series are showed at any one point. Because of the amount of series available, I am extending the functionality to checkboxes rather than just add them all on initiation and hide the majority initially as this would make the legend huge.
I would like to overlay a button on the chart to make it look integrated. This gives no problems in itself as I can give a negative value to legend.x to move it to make room for the button. However, this then poses a problem when more series are programmatically added, as the legend maintains its original width and I lose some options off to the side when there are too many.
This is a stripped down fiddle of my problem: https://jsfiddle.net/paLoxcy3/. This is a relevant snippet:
legend: {
align: "right",
x: -100
}
It's worth adding here the graph needs to maintain a responsive width, hence I cannot just add a width. As an aside, were I do to this (and indeed when the series names do eventually drop a line in my current fiddle), they then become left aligned which is not desired.
I've had a good play around with the options of legend but at this point assume the only solution to effectively add padding-right to the legend holder is to use chart.events.load and chart.events.redraw to somehow do it manually? It's a bit annoying as the options to add marginTop and
marginBottom exist but not marginLeft and marginRight.
Any help much appreciated! Shortcut to docs is here to save some time :)
I think that this problem is connected with small issue in Highcharts legend.renderItem function. As a workaround you can change if statement responsible for moving item to another line. Here you can see how this statement looks in code:
// if the item exceeds the width, start a new line
if (horizontal && legend.itemX - initialItemX + itemWidth >
(widthOption || (chart.chartWidth - 2 * padding - initialItemX - options.x))) {
legend.itemX = initialItemX;
legend.itemY += itemMarginTop + legend.lastLineHeight + itemMarginBottom;
legend.lastLineHeight = 0; // reset for next line (#915, #3976)
}
And Here you can see how I have changed this statement:
// if the item exceeds the width, start a new line
if (horizontal && legend.itemX - initialItemX + itemWidth >
(widthOption || (chart.chartWidth - 2 * padding - initialItemX - ((options.align === 'right') ? (-options.x) : options.x)))) {
legend.itemX = initialItemX;
legend.itemY += itemMarginTop + legend.lastLineHeight + itemMarginBottom;
legend.lastLineHeight = 0; // reset for next line (#915, #3976)
}
Here you can find Github topic connected with this issue:
https://github.com/highcharts/highcharts/issues/5443
And here you can see chart with my workaround:
https://jsfiddle.net/paLoxcy3/2/
Best regards.
Related
I am using AMCharts4 in a WordPress Project, everything works fine, but I am having a weird issue when I filter the data, let me show what's happening with images:
With All the data (Works 100% fine)
-Filtering the data (More than 2 results works fine 100%)
Filtering the data (2 or Less results not showing the Scores, here is the bug)
See how it's not showing the respective scores as the 2 first images.
The question here is how do I make those scores appear? I have tried commented lines of code in my funcions but nothing seems to work.
You could reproduce the issue in this link selecting the Australia or Brazil location for example
And see my function here
labelBullet = series.bullets.push(new am4charts.LabelBullet())
labelBullet.label.horizontalCenter = "left";
labelBullet.label.dx = 10;
labelBullet.label.text = "{values.valueX.workingValue.formatNumber('#.')}";
labelBullet.locationX = 1;
The issue is in your usage of labelBullet.locationX = 1;. If you take a look at am4 Bullets documentation:
There's one caveat, though. For bullets, locationY property means the
relative vertical position in the whole height of the column. That
means that if our scale would not start at zero, it would be not in
the direct center of the currently visible portion of the column.
As you are using inverted chart, same applies to locationX in your case. Meaning, label position is relative to the scale.
To fix the issue you can either set min for value axis to 0, as in this jsfiddle using:
valueAxis.min = 0;
or as mentioned in the document, push labels directly into column series template, as in this jsfiddle.
label = series.columns.template.createChild(am4core.Label);
label.text = "{values.valueX.workingValue.formatNumber('#.')}";
label.align = "left";
label.valign = "middle";
label.zIndex = 2;
I am using chart.js to try to create a timeline of events relative to current date.
The horizontal bar chart is close but would like to show only the tips of the bars eg as points which would pretty much be a horizontal line chart.
I have shown my horizontal bar chart along with a mock-up of what it would look like as horizontal line chart.
Is this possible with chart.js ?
You first need to know that every information that you can edit about the chart is stored in the variable containing your chart (called myChart usually, but my2Bar in your fiddle).
If you want to globally change the graph, you will need to edit attributes in myChart.config.options.
If you want to change a specific chart, you will need to edit attributes in myChart.config.data.
In this case, you need to change a specific chart (which is the horizontal bar).
If you happen to check the logs of your graph, and go very deep in the config, you will finally see that bars in your chart are drawn using attributes stored in myChart.config.data.datasets[0]._meta[0].data[n]._model (n being the nth rectangle drawn, top to bottom).
Some attributes you can find there :
base : The X position where the rectangle is starting to be drawn (0 in your xAxe for instance).
x : The rectangle is being drawn until this X position.
height : The height of the drawn rectangle.
and so on ...
To edit these values, you just need to loop in your different rectangles (the n in the above path).
But you just can't do it manually on the config of your variable. If you do this, it won't work since your chart is responsive (on resize, it will redraw the chart using the former options).
What you must use are Chart.js plugins.
Plugins let you handle all the events that are triggered while creating, updating, rendering your graph.
Then, in your beforeRender event (triggered after the initialisation, but before the drawing), you need to loop in your different rectangles to edit the values to affect how they will be drawn :
beforeRender: function(chart) {
for (var i = 0; i < chart.config.data.datasets[0]._meta[0].data.length; i++) {
// Change both `3` values to change the height & width of the point
chart.config.data.datasets[0]._meta[0].data[i]._model["base"] = chart.config.data.datasets[0]._meta[0].data[i]._model["x"] + 3;
chart.config.data.datasets[0]._meta[0].data[i]._model["height"] = 3;
}
}
Here is a jsFiddle with the final result.
Unfortunately, I wasn't able to make round dots, instead of squared ones.
Update :
I have also made another jsFiddle where all the dots are linked together which makes it look like it is a horizontal line chart (can be improved of course, but it is a good start).
I made a small fiddle based on the highcharts demos at http://jsfiddle.net/w53woene/1/
The issue I have boils down to four things:
1) I need to rotate the labels (this is based on a feature request so I can't ignore it).
labels: {
rotation: -45
}
2) The fact that the last label is really really long. This is based on data which is given for each chart so it's not always the case but it's the case around 30%-40% of the time so quite often.
3) Labels shouldn't be wrapped or shortened with an ellipsis.
4) I need to add spacing between the chart and the legend to adapt to the labels size but only when the label is going to be too long and colliding with the legend.
This only seems to happen when the chart: { inverted: true } and if it's not inverted the legend behaves normally, increasing the distance from the chart as needed.
Ideally I'd like to know if there's a specific option I'm missing that achieves this natively, i.e., I would have expected floating: false to achieve this the same way it does when the chart is not inverted, but it doesn't seem to. If this isn't possible I'd like to know how I can adjust the chart size (excluding the legend) in order to achieve this via JavaScript.
The problem is that vertical axis add space on the left size, but doesn't add on the bottom. I would wrap labels for a better readability: http://jsfiddle.net/w53woene/2/ - otherwise it may happen that you will have so long label that chart will get 0 pixels for plotting area.
If you really need non-wrapped text, then I would wrap Axis.prototype. getOffset to add extra space:
(function(H) {
H.wrap(H.Axis.prototype, 'getOffset', function(p) {
p.call(this);
if (this.isXAxis) {
var lastTick = this.tickPositions[this.tickPositions.length - 1],
lastLabel = this.ticks[lastTick].label,
height = lastLabel.getBBox(true).height;
this.labelDiff = height - this.chart.marginBottom;
if (this.labelDiff > this.chart.axisOffset[2]) {
this.chart.axisOffset[2] = this.labelDiff;
} else {
this.labelDiff = 0;
}
} else {
this.offset -= this.chart.xAxis[0].labelDiff;
}
});
})(Highcharts)
And live demo: http://jsfiddle.net/vwegeuvy/
Google have introduced a stock chart to their results which highlights "After hours":
I've had a hunt around the documentation and I can't see anything obvious that would suggest how to achieve a similar effect (even the line markers change colour). The closest I have found is this this jsfiddle (asgallant/apH2B/) which shows horizontal zoning -but doesn't alter the colour of the line.
Does anyone have any suggestions on how to achieve this effect?
I managed to achieve this effect by generating a div ontop of the chart (with an absolute position). Also made up some javascript to redraw the chart and add a Grey line in the selected interval.
Input function:
function updateShader() {
// gets the entered values from the fields
var start = Math.round(parseInt(document.getElementById('startPos').value))
var width = Math.round(parseInt(document.getElementById('interval').value))
// draws the chart again with the entered filters
drawBasic(start, width);
//converts the entered values to ISH pixels
start = Math.round(start * 5.125)
width = Math.round(width * 5.125)
// special case when start = end point, so it will be grey on a little on both sides
if (start == width) {
start = start + 69
} else {
start = start + 73
}
// changes the "cover-box" to fit the entered values
document.getElementById('cover').style.marginLeft = start + "px";
document.getElementById('cover').style.width = width - start + 73 + "px";
}
And the CSS for the cover box:
#cover {
position:absolute;
width:0px;
height:124px;
margin-top:38px;
margin-left:73px;
pointer-events:none;
background: rgba(0, 0, 0, 0.5);
z-index:2;
}
There are some flaws, like it allows for the "cover" box to be drawn outside the chart but overall this solution would work. Maybe throw in some more user-input checks.
I also changed the tooltip to be HTML, so I could change the z-index of it and via that generate it ontop of the cover box.
Finally, complete fiddle.
I'm building in some custom functionality where users can click on data points in a line chart to add notes to that date. This is a bit misleading as the notes aren't actually attached to the metrics themselves but rather the date it lands on. In other words, if I have 6 series on one line chart that spans the dates 01/01/12 - 01/08/12, a single note on 01/05/12 will apply to all 6 series. So, as you can imagine clicking on a data point on one of the 6 series or the date 01/05/12 would mislead the user to believe that this note would be applied to that data point, not the entire date and any series that lands on that date.
So, to remedy this usability issue I've decided that the best visual cue would be something like this:
There would be a clickable icon at the top of each xAxis gridLine that would need to scale with the xAxis gridLine (like if a user selects an area to zoom in on).
Suggestions on best way to pull this off? I only need a suggestion for how best to add the icon to every line... I have all post-click functionality already built.
Building on Mark's suggestion using redraw event to position the images and using load event to create them. Adding them on load is necessary to make them available during export and you would not want to create new images on each redraw either.
These chart events are used:
events: {
load: drawImages,
redraw: alignImages
}
In the drawImages function I'm using the inverse translation for the xAxis to position the images on the chart:
x = chart.plotLeft + chart.xAxis[0].translate(i, false) - imageWidth / 2,
y = chart.plotTop - imageWidth / 2;
and then adding them and setting a click handler, zIndex, pointer cursor:
chart.renderer.image('http://highcharts.com/demo/gfx/sun.png', x, y, imageWidth, imageWidth)
.on('click', function() {
location.href = 'http://example.com'
})
.attr({
zIndex: 100
})
.css({
cursor: 'pointer'
})
.add();
In alignImages the attr function is used to set new x and y values for the images which are calculated the in the same way as in drawImages.
Full example on jsfiddle
Screenshot:
Couple of ideas. First, I would use the chart redraw event to know when the chart is being redrawn (say on a zoom). Then second, explicitly place your images at the axis locations of interest. To get those query directly out of the DOM.
Using jQuery:
$('.highcharts-axis') //return an array of the two axis.
They will have svg "text element" children with (x, y) positions.