I'm using the jquery plugin jqplot for plotting some bar charts.
on hover, I'd like to display the tick for the bar and its value on a tooltip. I've tried
highlighter: { show: true,
showTooltip: true, // show a tooltip with data point values.
tooltipLocation: 'nw', // location of tooltip: n, ne, e, se, s, sw, w, nw.
tooltipAxes: 'both', // which axis values to display in the tooltip, x, y or both.
lineWidthAdjust: 2.5 // pixels to add to the size line stroking the data point marker
}
but it doesn't work. the bar visually gets lighter, and there's a small dot on the top (which would ideally go away--probably from line chart renderer stuff), but there is no tooltip anywhere. Anyone know how I can do this? I'll have lots of bars so the x-axis will be cluttered and kind of a mess if I show them down there only.
I go through jqplot.highlighter.js and find an undocumented property: tooltipContentEditor.
I use it to customize the tooltip to display x-axis label.
Use something like this:
highlighter:{
show:true,
tooltipContentEditor:tooltipContentEditor
},
function tooltipContentEditor(str, seriesIndex, pointIndex, plot) {
// display series_label, x-axis_tick, y-axis value
return plot.series[seriesIndex]["label"] + ", " + plot.data[seriesIndex][pointIndex];
}
nevermind, I did a roundabout way to create my own tooltip via jquery.
I left my highlighter settings as they were in my question (though you probably don't need the tooltip stuff).
In my js file after the bar chart is set up (after $.jqplot('chart', ...) I set up an on mouse hover binding, as some of the examples showed. I modified it like this:
$('#mychartdiv').bind('jqplotDataHighlight',
function (ev, seriesIndex, pointIndex, data ) {
var mouseX = ev.pageX; //these are going to be how jquery knows where to put the div that will be our tooltip
var mouseY = ev.pageY;
$('#chartpseudotooltip').html(ticks_array[pointIndex] + ', ' + data[1]);
var cssObj = {
'position' : 'absolute',
'font-weight' : 'bold',
'left' : mouseX + 'px', //usually needs more offset here
'top' : mouseY + 'px'
};
$('#chartpseudotooltip').css(cssObj);
}
);
$('#chartv').bind('jqplotDataUnhighlight',
function (ev) {
$('#chartpseudotooltip').html('');
}
);
explanation:
ticks_array is previously defined, containing the x axis tick strings. jqplot's data has the current data under your mouse as an [x-category-#, y-value] type array. pointIndex has the current highlighted bar #. Basically we will use this to get the tick string.
Then I styled the tooltip so that it appears close to where the mouse cursor is. You will probably need to subtract from mouseX and mouseY a bit if this div is in other positioned containers.
you can then style #chartpseudotooltip in your css. If you want the default styles you can just add it to .jqplot-highlighter-tooltip in the the jqplot.css.
hope this is helpful to others!
I am using the version of the highlighter plugin on the following link:
https://github.com/tryolabs/jqplot-highlighter
The parameters I am using:
highlighter: {
show:true,
tooltipLocation: 'n',
tooltipAxes: 'pieref', // exclusive to this version
tooltipAxisX: 20, // exclusive to this version
tooltipAxisY: 20, // exclusive to this version
useAxesFormatters: false,
formatString:'%s, %P',
}
The new parameters ensure a fixed location where the tooltip will appear. I prefer to place it on the upper left corner to avoid problems with resizing the container div.
Related
By default, JSXGraph's board renders with a grid in the background. When I instantiate it with the option axis: false, the grid disappears, but so do the axis labels and tickmarks.
Messing with JXG.Options like this doesn't seem to do anything:
JXG.Options.grid.strokeColor = '#ff0000';
JXG.Options.grid.strokeOpacity = 0;
The grid board option doesn't actually turn off the grid, just makes it behave differently on zoom, noted here.
Basically, making the board like this turns off the axis ticks and the grid for the Y axis:
var board = JXG.JSXGraph.initBoard(id, {
axis: true,
defaultAxes: {
y: { ticks: { visible: false } }
}
});
And changing visible to true makes the grid and the ticks visible.
How do I turn off just the background grid, leaving the axis labels intact?
Alright, I figured this out by looking at options.js in jsxgraph. The main grid is actually referred to as "ticks", closely related to the ticks on the axis line. In fact, it's drawn with the same piece of code I'm guessing. There's minorHeight and majorHeight for the ticks, referring to either ticks on the axis line or ticks on the entire background. minorHeight is 10 by default, and majorHeight is -1, a special case that means draw on the whole board. The solution is to change majorHeight so it draws like the minor ticks, just a little differently for clarity:
var board = JXG.JSXGraph.initBoard(id, {
axis: true,
defaultAxes: {
y: { ticks: { visible: true, majorHeight: 5 } }
}
});
I wanted to ask, if there is a way to push chart to right side, so there will not be any free space. I attached simple image, red line show, that this space should be filled also by chart.
Also is possible to create vertical lines as dots ? I can't find answers to my questions, on official doc.
An this is my javsacript code:
var lineChartData = {
labels : ["January","February","March","April"],
datasets : [
{
label: "Dataset",
pointHighlightStroke : "rgba(220,220,220,1)",
data : [0,3,4,11]
}
]
};
window.onload = function(){
var ctx = document.getElementById("canvas").getContext("2d");
window.myLine = new Chart(ctx).Line(lineChartData, {
responsive: true,
scaleOverride: true,
scaleSteps: Math.ceil((max-start)/step),
scaleStepWidth: step,
pointDot : false,
});
}
The space is there because the last x axis label (April). Chart.js leaves enough space so that the label does not get clipped off. This also ensures that the tooltip for the last point shows without clipping off.
You could set the last (or all) labels to an empty string to get rid of the space. However, you won't see any labels in the tooltips. If you want to see the labels in the tooltips you need to extend the chart to remove the x axis labels like so
Chart.types.Line.extend({
name: "LineAlt",
initialize: function (data) {
var labels = data.labels;
data.labels = data.labels.map(function () { return '' });
Chart.types.Line.prototype.initialize.apply(this, arguments);
this.datasets[0].points.forEach(function (point, i) {
point.label = labels[i]
})
}
});
Note that you need to use LineAlt instead of Line.
Fiddle - http://jsfiddle.net/0u2c7tez/
However this will still clip off the tooltip for the last point. If you are going to enable tooltips and don't want them to be clipped off, then you need to use custom tooltips so that the tooltip is rendered in an external element (instead of the canvas) and not clipped off (see https://github.com/nnnick/Chart.js/blob/master/samples/line-customTooltips.html)
I have a c3.js line graph that represents the evolution of 2 values. I need that the tooltip of the line graph to be a pie chart (tooltip = another c3.js graph).
Here is what I succeeded:
http://jsfiddle.net/owhxgaqm/80/
// c3 - custom tooltip
function generateGraph(data1,data2) {
console.log(data1.name + '\t' + data1.value + '\t' + data2.name + '\t' + data2.value);
var chart1 = c3.generate(
{
bindto: "#t",
data: {columns : [[data1.name, data1.value],[data2.name, data2.value]],
type : 'pie'}
});
}
var chart = c3.generate({
data: {
columns: [
['data1', 1000, 200, 150, 300, 200],
['data2', 400, 500, 250, 700, 300], ]
},
tooltip: {
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
generateGraph(d[0], d[1]);
var divt = document.getElementById("t");
return '';
}
}
});
As you can see I'm binding the "tooltip" with an already existing div so this is not really what I want from c3.js.
Any idea is welcome.
Thanks.
Adding a Chart inside a C3 Tooltip
You can use the tooltip element that c3 already has. In your contents function call the generateGraph function (see next step). Pass in the tooltip element available in this.tooltip in addition to the data.
...
tooltip: {
contents: function (d) {
// this creates a chart inside the tooltips
var content = generateGraph(this.tooltip, d[0], d[1])
// we don't return anything - see .html function below
}
}
...
Your generateGraph function basically creates a c3 chart in your tooltip element (bindto supports a d3 element). We do a bit of optimization (if the data is same, the chart is not recreated) and cleanup (when a chart is recreated it is destroyed and removed from the DOM)
function generateGraph(tooltip, data1, data2) {
// if the data is same as before don't regenrate the graph - this avoids flicker
if (tooltip.data1 &&
(tooltip.data1.name === data1.name) && (tooltip.data1.value === data1.value) &&
(tooltip.data2.name === data2.name) && (tooltip.data2.value === data2.value))
return;
tooltip.data1 = data1;
tooltip.data2 = data2;
// remove the existing chart
if (tooltip.chart) {
tooltip.chart = tooltip.chart.destroy();
tooltip.selectAll('*').remove();
}
// create new chart
tooltip.chart = c3.generate({
bindto: tooltip,
size: {
width: 200,
height: 200
},
data: {
columns: [[data1.name, data1.value], [data2.name, data2.value]],
type: 'pie'
}
});
// creating a chart on an element sets its position attribute to relative
// reset it to absolute (the tooltip was absolute originally) for proper positioning
tooltip.style('position', 'absolute');
}
Note that we set the chart size so that it's more like tooltip content instead of a subchart.
The last bit is a bit hacky - since c3 requires that we set a HTML (which we don't want to do) and because we don't have any other callbacks we can easily hitch onto after the content handler, we have to disable the function that c3 uses to set the html content on the tooltip (this will affect only this chart's tooltip) i.e. .tooltip.html
// MONKEY PATCHING (MAY break if library updates change the code that sets tooltip content)
// we override the html function for the tooltip to not do anything (since we've already created the tooltip content inside it)
chart.internal.tooltip.html = function () {
// this needs to return the tooltip - it's used for positioning the tooltip
return chart.internal.tooltip;
}
Fiddle - http://jsfiddle.net/muuqvf1a/
Tooltip Positioning
Instead of using c3's tooltip positioning you could also size and position the tooltip at the bottom of the chart. Just style .c3-tooltip-container.
Alternatives
Note that c3 also support subcharts (http://c3js.org/reference.html#subchart-show) and data.mouseover (http://c3js.org/reference.html#data-onmouseover) which could also be a cleaner avenues worth exploring.
I was having a hard time trying to figure out how to center labels on a datetime x-axis in Highcharts without using categories and tickPlacement (since tickPlacement only works on categories).
My axis was dynamically created so I could not simply set an x-offset or padding, as this would cause axes of different intervals to look strange.
After messing around with the config options I think I may have found a solution using the x-axis formatter and some css / jquery noodling in the Highcharts callback. See my answer below.
The trick is to use the x-axis labels object like this:
xAxis: {
type: 'datetime',
labels: {
useHTML: true,
align: 'center',
formatter: function () {
//using a specific class for the labels helps to ensure no other labels are moved
return '<span class="timeline_label">' + Highcharts.dateFormat(this.dateTimeLabelFormat, this.value) + '</span>';
}
}
You can see that the formatter will keep whatever dateTimeLabelFormat has been set by the user or default.
Then have a callback that does something like this:
function (chart) {
var $container = $(chart.container);
var $labels = $container.find('.highcharts-axis-labels .timeline_label');
var $thisLabel, $nextLabel, thisXPos, nextXPos, delta, newXPos;
$labels.each(function () {
$thisLabel = $(this).parent('span');
thisXPos = parseInt($thisLabel.css('left'));
$nextLabel = $thisLabel.next();
nextXPos = $nextLabel.length ? parseInt($nextLabel.css('left')) : chart.axes[0].left + chart.axes[0].width;
delta = (nextXPos - thisXPos) / 2.0;
newXPos = thisXPos + delta;
if ($nextLabel.length || $(this).width() + newXPos < nextXPos) {
$thisLabel.css('left', newXPos + 'px');
} else {
$thisLabel.remove();
}
});
});
In short, this will go through each label and determine how much it should be moved over (using css) by calculating the distance between itself and the next label. When it reaches the the last label, it either moves it over using the end of the axis for the calculation or removes it if it won't fit. This last part is just the decision I decided to make, you can probably choose to do something else like word wrap, etc.
You can see the jsfiddle here
Hope this helps some people. Also, if there are any improvements it would be great to see them here.
Based on the existing answer, there is a much simpler solution that also works when resizing the browser window (or otherwise forcing the chart to redraw), even when the tick count changes: http://jsfiddle.net/McNetic/eyyom2qg/3/
It works by attaching the same event handler to both the load and the redraw events:
$('#container').highcharts({
chart: {
events: {
load: fixLabels,
redraw: fixLabels
}
},
[...]
The handler itself looks like this:
var fixLabels = function() {
var labels = $('div.highcharts-xaxis-labels span', this.container).sort(function(a, b) {
return +parseInt($(a).css('left')) - +parseInt($(b).css('left'));
});
labels.css('margin-left',
(parseInt($(labels.get(1)).css('left')) - parseInt($(labels.get(0)).css('left'))) / 2
);
$(labels.get(this.xAxis[0].tickPositions.length - 1)).remove();
};
As you see, the extra wrapping of labels is unnecessary (at least if you do not have more than one xAxis). Basically, it works like this:
Get all existing labels (when redrawn, this includes newly added ones). 2. Sort by css property 'left' (they are not sorted this way after some redrawing)
Calculate offset between the first two labels (the offset is the same for all labels)
Set half of the offset as margin-left of all labels, effectively shifting them half the offset to the right.
Remove the rightmost label (moved outside of chart, by sometimes partly visible).
I'm using the Cursor plugin to display a vertical line on a jqplot chart. The tooltip for the Cursor plugin is showing the X and Y values.
I want to add a piece of meta data to the plot points.
[x,y,1337] where 1337 is the meta deta.
I want to modify the Cursor plugin tooltip to show this metadeta as well as the data it already displays.
Use case: I have multiple series that have been scaled to 0-100 across all series for trending. I need to display the unscaled value.
Update:
i've got it working by hacking up jqplot.cursor.js, is there a better way?
Line 468: function updateToolTip(gridpos, datapos, plot){
// ...
s += $.jqplot.sprintf(c.tooltipFormatString, label, sx, sy, data[2]);
This is how I override the tooltipContentEditor jqplot function, it works great.
highlighter: {
show: true,
showMarker:true,
showTooltip:true,
sizeAdjust: 10,
tooltipLocation: 'se',
tooltipAxes: 'xy',
yvalues: 1,
formatString:'<table class="jqplot-highlighter"><tr><td>date:</td><td>%s</td></tr><tr><td>PiecesPerHour:</td><td align="right">%s</td></tr></table>',
useAxesFormatters: true,
tooltipContentEditor: function(str, seriesIndex, pointIndex, plot){
var data = plot.series[seriesIndex].data[pointIndex];
var label = plot.legend.labels[seriesIndex].indexOf('Target')
var format = [];
//A little formatting to the data before I join it to the Html string
if (that.model.get('groupBy')==='month')
format[0] = new Date(data[0] + 1000*60*60*24).format('mmmm yyyy');
else
format[0] = new Date(data[0] ).format('mmmm dd, yyyy');
format[1] = new Number(data[1]).toFixed(1)
//join the data to the Html string:
str = $.jqplot.sprintf.apply($.jqplot.sprintf, [str].concat(format));
return str;
}
}
Basically you get the Series and Point data and join it to an Html string with sprintf and then return the string.