I have created a chart using C3.JS. It has bar graph with line graph like as shown below
The graph is working fine but I have a requirement that I need a line graph to be shown within the tooltip along with other data points.
The line graph is coming but the other data points are missing (x, data1, data2). In addition tooltip should come only on mouse click not mouse over also by default when the page loads the tooltip should appear for the last bar.
Current my tooltip shows only with the line graph like as shown below without the other data points(x, data1, data2)
But I expect my tooltip to appear like as shown below
My code is as given below
Can anyone please help me on this
Working JSFiddle
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,
padding: {
right: 15
},
size: {
width: 200,
height: 200
},
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250, 160],
['data2', 50, 20, 10, 40, 15, 25, 34]
]
},
tooltip: {
show: false
}
});
// 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');
tooltip.style('background-color', 'white');
}
var chart = c3.generate({
data: {
columns: [
['x', 1000, 200, 150, 300, 200],
['data1', 1000, 200, 150, 300, 200],
['data2', 400, 500, 250, 700, 300], ],
axes: {
'data1': 'y2'
},
type: 'bar',
types: {
'data1': 'line'
}
},
tooltip: {
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
// 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
}
}
});
// 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;
}
Live Demo:
http://jsfiddle.net/blackmiaool/y7Lhej4m/
Instead of overriding the .html function of tooltip, I use contents and css to customize the tooltip.
js:
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
generateGraph(this.tooltip, d[0], d[1]);
var tip = this.tooltip[0][0].querySelector(".c3-tooltip");//find previous tooltip
if (tip) {
tip.parentElement.removeChild(tip);//remove it
}
return this.getTooltipContent.apply(this, arguments) + this.tooltip.html();//concat default tooltip and customized one
}
css:
.c3-tooltip-container {
max-height: none !important;
}
.c3-tooltip {
box-shadow: none;
width: 100%;
}
.c3-tooltip tr {
border-left: none !important;
border-right: none !important;
}
.c3-tooltip td.name {
border-left: none !important;
}
.c3-tooltip tr:first-child {
display: none;
}
.c3-tooltip-container .c3-chart-line {
opacity: 1 !important;
}
Related
I have a C3.js bar graph and I am trying to add some spacing in between the x-axis and legend in order to avoid the following visual unpleasantry:
I have already tried to do the following and it has had no effect on the graph:
#stacked_bar_chart_container .c3-axis-x {
margin-bottom: 5px;
}
I have even tried to do the same thing with .c3-legend-item but with a margin-top: 5px; instead, but it has still had no effect.
I would also like to note that by removing #stacked_bar_chart_container (the div container that this graph is binded to) there was still no effect either.
You can adjust the space between the chart and bottom legend by using padding: {bottom: 20}, which will add additional 20px between chart and legend. If you place the legend on the right side, you can use padding: {right: 20}.
Because padding will make your chart smaller, you may want to adjust the height or width of your chart by using size: {height: 480} or size: {width: 640}, respectively.
I have created a fiddle example http://jsfiddle.net/6jztrbk7/
This is the example JavaScript code:
var chart = c3.generate({
data: {
columns: [
['data1', -30, 200, 200, 400, -150, 250],
['data2', 130, 100, -100, 200, -150, 50],
['data3', -230, 200, 200, -300, 250, 250],
type: 'bar',
groups: [
['data1', 'data2']
]
},
grid: {
y: {
lines: [{value:0}]
}
},
legend: {
position: 'bottom'
},
size: {
height: 300 //adjust chart height
},
padding: {
bottom: 20 //adjust chart padding bottom
}
});
There are some way to set the position for the tooltip in c3js, because the tooltip is always at the right side of the vertical line, but when the cursor is on the last value then it changes position towards left side or state in the graph without leaving it.
I know that this is normal tooltip behavior on c3js but I want that the tooltip to keep its position on the right side for the last value
you can see the example on this link
var chart = c3.generate({
data: {
columns: [
['data1', 300, 350, 300, 0, 0, 0],
['data2', 130, 100, 140, 200, 150, 50]
],
types: {
data1: 'area',
data2: 'area-spline'
}
},
axis: {
y: {
padding: {bottom: 0},
min: 0
},
x: {
padding: {left: 0},
min: 0,
show: false
}
}
});
any ideas?
You can set the tooltip position using this code:
http://c3js.org/reference.html#tooltip-position
tooltip: {
position: function (data, width, height, element) {
var top = d3.mouse(element)[1] + 15;
return {top: top, left: parseInt(element.getAttribute('x')) + parseInt(element.getAttribute('width'))}
}
}
However how it works when the tooltip pokes out the side of the chart I don't know, it may just be cutoff. You could add a margin of 100px to the right if needed I suppose but that makes your chart smaller.
I was wondering if anyone know how to round the corners of the columns in a c3.js bar chart? The hacks I have found online, doesn't seem to work, so I hope somewhere here can help!
Thank you :)
var mph_chart = c3.generate({
bindto: '#mph_chart',
padding: {
top: 10,
right: 50,
bottom: 10,
left: 190
},
bar: {
width: 35,
},
color: {
pattern: ['#3366ff']
},
data: {
x: 'x',
labels:true,
columns: [
['x', 'Porsche Macan','Porsche Macan S','Porsche Macan S Diesel','Porsche Macan Turbo','Porsche Macan GTS'],
['0-62 mph in seconds', 6.7, 5.4, 6.3, 4.8, 5.2]
],
type: 'bar',
},
legend: {
show: true
},
axis: {
rotated: true,
x: {
type: 'category',
tick: {
multiline: false
} // this needed to load string x value
}
},
tooltip: {
show:false
},
bar: {
width: {
ration: .7
},
spacing: 2
}
});
I have tried it for simple bar charts, which provides bars with rounded corner as shown below:-
Here is the jsFiddle:- jsFiddle: Rounded bar charts
.c3-legend-item-tile, .c3-xgrid-focus, .c3-ygrid, .c3-event-rect, .c3-bars path {
shape-rendering: auto;
}
You need to basically override certain c3.js functions and provide logic for getting rounded corner by providing multiple points near the corner.
Otherwise all bars in a bar chart are polygons with 4 points. Solution is to provide bars with more points.
I'm trying to create a line and bar chart using the NVD3 javascript library. I have an interesting problem where the lines load perfectly fine, but the bars are omitted on initial page load. But, once I resize the window or do an inspect element n the chart, the bar chart loads.
Here is my code:
// raw #timeline is just JSON
// This is a rails app
var timeline = <%=raw #timeline %>
var chart_concurrent_users_data = [
{
values: timeline.map(function(d) { return {x: d[0], y: d[1] } }),
key: 'Concurrent Users',
color: '#C0C0C0',
yAxis: 2,
bar: true
},
{
values: timeline.map(function(d) { return {x: d[0], y: d[2] } }),
key: 'User Joins',
color: '#5cb85c',
yAxis: 1
},
{
values: timeline.map(function(d) { return {x: d[0], y: d[3] } }),
key: 'User Leaves',
color: '#006699',
yAxis: 1
}
];
// User Chart / Leave / Joins
nv.addGraph(function() {
var chart_users = nv.models.linePlusBarChart()
.margin({top: 30, right: 80, bottom: 50, left: 80})
.color(d3.scale.category10().range());
// FULL Graph
chart_users.xAxis.tickFormat(function(d) {
return d ? d3.time.format('%H:%M%')(new Date(d)) : '';
});
chart_users.xAxis.axisLabel('Time');
chart_users.y2Axis
.axisLabel('Users joining / leaving')
.tickFormat(d3.format(',.'));
chart_users.y1Axis
.axisLabel('Concurrent Users')
.tickFormat(d3.format(',.'));
chart_users.bars.forceY([0]);
chart_users.lines.forceY([0]);
d3.select('#chart_users svg')
.datum(chart_concurrent_users_data)
.call(chart_users);
nv.utils.windowResize(chart_users.update);
return chart_users;
});
I've tested this in both chrome and firefox.
Here are some images of the problem.
On initial load:
After resize:
Note I can also resize it back to full screen and the bar chart is still present. Any ideas are greatly appreciated.
My plunker NVD3 chart
I'm using NVD3's multiChart chart. When I add additional area graphs into the chart, it appears that the legend pushes down the chart. The more graphs get added the more the chart appears scrunched.
Note how the Y axis in the 2 screenshot is almost 50% shorter than the original.
I tried removing the legend with CSS, but the chart still shortens:
.legendWrap {
display: none;
}
Even removed the legend with Javascript and still the chart gets scrunched:
removeElementsByClass('nv-legend');
function removeElementsByClass(className){
var elements = document.getElementsByClassName(className);
while(elements.length > 0){
elements[0].parentNode.removeChild(elements[0]);
}
}
Any idea how to solve this problem?
My full drawChart function:
function drawChart(res) {
console.log('res',res);
nv.addGraph(function() {
chart = nv.models.multiChart()
.margin({
top: 20,
right: 40,
bottom: 50,
left: 40
})
.interpolate("linear") // don't smooth out the lines
.color(d3.scale.category10().range());
chart.xAxis.tickFormat(function(d) {
return d3.time.format('%I:%M')(new Date(d));
});
chart.yAxis1.tickFormat(d3.format(',f'));
chart.yAxis2.tickFormat(function(d) {
return '$' + d3.format(',f')(d)
});
d3.select('svg#chart')
.datum(res)
.transition().duration(500).call(chart);
chart.tooltip.hidden(true);
chart.update();
d3.select('.lines2Wrap').node().parentNode.insertBefore(d3.select('.stack1Wrap').node(), d3.select('.lines2Wrap').node());
// Add top padding to xAxis timeline:
d3.selectAll('.nv-x.nv-axis > .nv-wrap.nv-axis > g > g.tick > text').each(function(d,i) {
d3.select(this).attr('dy', '1.5em');
});
d3.selectAll('.nv-x.nv-axis > .nv-wrap.nv-axis > .nv-axisMaxMin > text').each(function(d,i) {
d3.select(this).attr('dy', '1.5em');
});
nv.utils.windowResize(chart.update);
return chart;
});
}
You are correct, the legend is the cause of the problem. Instead of setting display: none in CSS, you can remove the legend entirely like this:
chart = nv.models.multiChart()
.margin({
top: 20,
right: 40,
bottom: 50,
left: 40
})
.showLegend(false) // don't show the legend
.interpolate("linear") // don't smooth out the lines
.color(d3.scale.category10().range());
Full working code here.
Anychart multichart complete code with custom theme
<!doctype html>
<html>
<head>
<script src="https://cdn.anychart.com/js/7.14.3/anychart-bundle.min.js"></script>
<link rel="stylesheet" href="https://cdn.anychart.com/css/latest/anychart-ui.min.css">
<div id="container"></div>
<style>
html, body, #containerss {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="containerss"></div>
<script type="text/javascript">
anychart.onDocumentReady(function() {
// create variable for custom theme
var customTheme = {
// define settings for bar charts
"column": {
// set chart title
"title": {
"text": "Column Chart",
"enabled": true
},
// settings for default x axis
"defaultXAxisSettings": {
// set x axis title
"title": {
"text": "Retail Channel",
"enabled": true
}
},
// settings for default y axis
"defaultYAxisSettings": {
// set axis name
"title": {
"text": "Sales",
"enabled": true
}
}
}
};
// data
var data = anychart.data.set([
["Department Stores", 63, 87,87],
["Discount Stores", 72, 66,42],
]);
// apply custom theme
anychart.theme(customTheme);
// append a theme
anychart.appendTheme(additional_theme);
// set chart type
chart = anychart.column();
// set data
seriesData_1 = data.mapAs({x: [0], value: [1]});
seriesData_2 = data.mapAs({x: [0], value: [2]});
seriesData_3 = data.mapAs({x: [0], value: [3]});
chart.column(seriesData_1);
chart.column(seriesData_2);
chart.column(seriesData_3);
// set container and draw chart
chart.container("containerss");
chart.draw();
});
additional_theme = {
"column": {
"palette": {
"type": "distinct",
"items": ["red", "#8a9597",'#000']
}
}
};
</script>
</body>
</html>