Reputation: 1270
I'm using Bootstrap 3 tabs for a page layouts and Chart.js to create donut graphs for a project.
However the charts do not load when changing to a tab with a chart on it. Sometimes they load when you start inspecting elements in google chrome though. They only seem to render if they are loaded on the first visible tab.
There is one known error with the chart.js javascript in the chrome console:
Uncaught IndexSizeError: Failed to execute 'arc' on 'CanvasRenderingContext2D': The radius provided (-0.5) is negative.
And I think this is because Bootstrap has the tabs visibility set to none and therefore the chart doesn't render properly or something...
It should be looking like this:
Has anyone else had this problem & if so; is there a workaround?
... Or should I look for another charting script that will play nicely with Bootstrap tabs.
Thank you in advance :)
Upvotes: 13
Views: 13348
Reputation: 762
I have found a rather easy and somewhat lazy option. You can set all your tabs to "tab-pane active" This way all your charts on the tabs show and render, then
setTimeout(yourFunction, 250);
function yourFunction()
{
$('.nav-tabs a[href="#anytabExceptdefault"]').tab('show')
$('.nav-tabs a[href="#backtoyourdefaultTab"]').tab('show')
Upvotes: 0
Reputation: 731
Since the origin of this problem is display none of tab-content in bootstrap I've solved this with a little css hack.
.tab-content>.tab-pane {
display: block;
height: 0;
overflow: hidden;
}
.tab-content>.tab-pane.active {
height: auto;
}
Upvotes: 21
Reputation: 12577
To me the solution is dead simple. Using the latest commit, update the Chart.Arc function as follows:
Chart.Arc = Chart.Element.extend({
inRange: function (chartX, chartY) {
var pointRelativePosition = helpers.getAngleFromPoint(this, {
x: chartX,
y: chartY
});
//Check if within the range of the open/close angle
var betweenAngles = (pointRelativePosition.angle >= this.startAngle && pointRelativePosition.angle <= this.endAngle),
withinRadius = (pointRelativePosition.distance >= this.innerRadius && pointRelativePosition.distance <= this.outerRadius);
return (betweenAngles && withinRadius);
//Ensure within the outside of the arc centre, but inside arc outer
},
tooltipPosition: function () {
var centreAngle = this.startAngle + ((this.endAngle - this.startAngle) / 2),
rangeFromCentre = (this.outerRadius - this.innerRadius) / 2 + this.innerRadius;
return {
x: this.x + (Math.cos(centreAngle) * rangeFromCentre),
y: this.y + (Math.sin(centreAngle) * rangeFromCentre)
};
},
draw: function (animationPercent) {
var easingDecimal = animationPercent || 1;
var ctx = this.ctx;
var innerRadius = (this.innerRadius < 0) ? this.innerRadius * -1 : this.innerRadius;
var outerRadius = (this.outerRadius < 0) ? this.outerRadius * -1 : this.outerRadius;
ctx.beginPath();
ctx.arc(this.x, this.y, outerRadius, this.startAngle, this.endAngle);
ctx.arc(this.x, this.y, innerRadius, this.endAngle, this.startAngle, true);
ctx.closePath();
ctx.strokeStyle = this.strokeColor;
ctx.lineWidth = this.strokeWidth;
ctx.fillStyle = this.fillColor;
ctx.fill();
ctx.lineJoin = 'bevel';
if (this.showStroke) {
ctx.stroke();
}
}
});
The thing to take note of is the ternary operator on both inner and outer radius to prevent negative values. This worked like a charm on my project. Charts continued to be responsive and there was NO deleterious effects by converting the radii into natural numbers (ie: positive)
Upvotes: 1
Reputation: 503
Got this fiddle on google search.This might help. http://jsfiddle.net/woqsazau/2/
HTML :
<div class="container">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist" id="bs-tabs">
<li><a href="#length" role="tab" data-toggle="tab">Length</a>
</li>
<li class="active"><a href="#height" role="tab" data-toggle="tab">Height</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div class="tab-pane" id="length">
<div class="canvas-holder">
<canvas id="lengthChart" class="chart"></canvas>
</div>
</div>
<div class="tab-pane active" id="height">
<div class="canvas-holder">
<canvas id="heightChart" class="chart"></canvas>
</div>
</div>
</div>
</div>
JS :
function getJSON() {
var data = {
"Series": {
"height": [{
"Date": "2014-09-19",
"Value": 85
}, {
"Date": "2014-09-23",
"Value": 74
}]
}
};
console.log(data);
var series = {};
// This loop is looping across all the series.
// x will have all the series names (heights, lengths, etc.).
for (var x in data.Series) {
var dates = [];
var values = [];
// Loop across all the measurements for every serie.
for (var i = 0; i < data.Series[x].length; i++) {
var obj = data.Series[x][i];
// Assuming that all the different series (heights, lengths, etc.) have the same two Date, Value attributes.
dates.push(moment(obj.Date).format("MMMM Mo"));
values.push(obj.Value);
}
// Keep the list of dates and values by serie name.
series[x] = {
dates: dates,
values: values
};
}
console.log(series.height.dates);
console.log(series.height.values);
//---------
//valueArray.push(Value);
//dateArray.push(moment(date).format("MMMM Mo"));
var dataArray = {
labels: series.height.dates,
datasets: [{
label: "Height",
strokeColor: "rgb(26, 188, 156)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
data: series.height.values
}]
};
function chartFunc(dataArray) {
var ctx = document.getElementById("heightChart").getContext("2d");
var myLineChart = new Chart(ctx).Line(dataArray, {
scaleShowGridLines: true,
bezierCurve: true,
bezierCurveTension: 0.4,
datasetStroke: false,
fillColor: "rgba(0,0,0,0)",
datasetFill: false,
responsive: true,
showTooltips: true,
animation: false
});
} //chartFunc
chartFunc(dataArray);
} //getJSON
$(document).ready(function () {
getJSON();
$(document).on('shown.bs.tab', 'a[data-toggle="tab"]', function (e) {
console.log("tab changed");
});
});
Upvotes: 0
Reputation: 478
I have had this problem. If I remember correctly I solved it by calling the chart js render when the active tab is shown using shown.bs.tab
(http://getbootstrap.com/javascript/#tabs)
i.e.
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
e.target // newly activated tab
//call chart to render here
});
Hope that helps.
Upvotes: 10