问题
I'm trying to use ChartsJS within a tabbing system. The first chart in the first tab is rendered, but subsequent charts are not.
I believe this is because the tabs have display:none
, so when the charts are first generated they are created in a div with zero dimensions.
This fixes the issue, but breaks the tabs:
.vertical-nav>div.tab-content {
display: block !important;
}
With that in mind I've tried all kinds of ways to regenerate the charts after the tab is open, or force display:block
just before generating the chart. Nothing works.
Here's my attempt to regenerate the charts:
jQuery(document).ready(function() {
jQuery('.vertical-nav ul li span').click(function(){
// Target the canvas within the correct tab
var tabIndex = jQuery(this).parent().index();
var canvasID = jQuery('.vertical-nav .tab-content:eq( ' + tabIndex + ' ) canvas').attr('id');
theChart = charts[canvasID].chart;
builtChart = charts[canvasID].builtChart;
// Destroy all of the charts so that we can rebuild them
for (var key in charts) {
if (charts.hasOwnProperty(key)) {
charts[key].builtChart.destroy();
}
}
// get the chart data and options from the charts object
newData = charts[canvasID].data;
newOptions = charts[canvasID].options;
// create the new chart
newChart = document.getElementById(canvasID).getContext("2d");
new Chart(newChart).Line(newData, newOptions);
});
})
The staging site is: http://telcomplusplc.uberleaf.com/company/?tab=our-growth
I've even tried setting a timeout to delay the chart generation until the tab is displayed, but no luck.
You can see in the URL above that the first chart generates and regenerates as it should. It's just the rest of them that don't (presumably because they are with a display:none
div on first generation).
Any help is much appreciated. Happy to give more info if needed.
Update
When the window is resized, the chart is redrawn. Here's the code for that (in chart.js):
// Attach global event to resize each chart instance when the browser resizes
helpers.addEvent(window, "resize", (function(){
// Basic debounce of resize function so it doesn't hurt performance when resizing browser.
var timeout;
return function(){
clearTimeout(timeout);
timeout = setTimeout(function(){
each(Chart.instances,function(instance){
// If the responsive flag is set in the chart instance config
// Cascade the resize event down to the chart.
if (instance.options.responsive){
instance.resize(instance.render, true);
}
});
}, 50);
};
})());
I think what I need is to be able to fire that from a click function. I've tried an failed, my JS skills aren't that good.
回答1:
If you use display:none then chartjs doesn't know the size to make the chart and then doesn't. If you switch to jquery.hide()/show() it will work. Here's a jsfiddle
*The fiddle works without it but if you have a lot of content then you may need to also hide the area temporarily.
Javascript
var data1 = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{ data: [65, 59, 80, 81, 56, 55, 40] }]
};
var data2 = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{ data: [65, 59, 80, 81, 56, 55, 40] }]
};
// Load each chart and hide() the tab immediately.
var ctx = document.getElementById('chart1').getContext('2d');
var chart1 = new Chart(ctx).Bar(data1);
$('#tab1').hide();
var ctx = document.getElementById('chart2').getContext('2d');
var chart2 = new Chart(ctx).Line(data2);
$('#tab2').hide();
/*
// will not work because chart is not rendered
$('#tab1_btn').on('click',function(){
$('#tab1').css('display','block');
$('#tab2').css('display','none') })
$('#tab2_btn').on('click',function(){
$('#tab1').css('display','none');
$('#tab2').css('display','block') })
*/
$('#tab1_btn').on('click',function(){
$('#tab1').show();
$('#tab2').hide()
})
$('#tab2_btn').on('click',function(){
$('#tab1').hide();
$('#tab2').show()
})
// if you have a lot of content in multiple tabs
// it may be necessary to temporarily cover the content
$('#tab_cover').hide();
HTML
<button id="tab1_btn">tab1</button>
<button id="tab2_btn">tab2</button>
<div id="tab_cover"> </div>
<div id="tabs">
<div id="tab1" class="tab">
<canvas id="chart1" class="chart" width="400" height="300"></canvas>
</div>
<div id="tab2" class="tab">
<canvas id="chart2" class="chart" width="400" height="300"></canvas>
</div>
</div>
CSS
/* will not work because chart is not rendered
.tab { display:none } */
.chart { width:50% }
.float { float:right; width:50%; }
#tab_cover { background:#9ff; height: 100% !important; width:100%; position: absolute; z-index: 300; }
回答2:
I've found a solution. I was close with the code I had in the question. It seems that I need to destroy and resize the charts.
I also had to change the display
status of the tabs manually.
Here's what works for me:
function createNewChart(canvasID) {
// get the chart data and options from the charts object
newData = charts[canvasID].data;
newOptions = charts[canvasID].options;
// create the new chart
theChart = document.getElementById(canvasID).getContext("2d"); console.dir(theChart);
new Chart(theChart).Line(newData, newOptions);
}
jQuery('.vertical-nav ul li').click(function(){
// Target the canvas within the correct tab
var tabIndex = jQuery(this).index();
var canvasID = jQuery('.vertical-nav .tab-content:eq( ' + tabIndex + ' ) canvas').attr('id');
theChart = charts[canvasID].chart;
builtChart = charts[canvasID].builtChart;
// Destroy all of the charts so that we can rebuild them
for (var key in charts) {
if (charts.hasOwnProperty(key)) {
jQuery('.vertical-nav .tab-content:eq( ' + tabIndex + ' )').css('display', 'block');
jQuery('.vertical-nav .tab-content:eq( ' + tabIndex + ' ) .tab-content-chart').css('display', 'block');
charts[key].builtChart.destroy();
charts[key].builtChart.resize();
}
}
createNewChart(canvasID);
});
回答3:
I was running into the same issues when using hidden divs that only display upon certain selections from a drop-down. What I found to work is similar to what ow3n did. I created the graphs and then had them only generate once the selection was made.
HTML:
<div>
<select>
<option value="option-1">Option 1</option>
<option value="option-2">Option 2</option>
<option value="option-3">Option 3</option>
</select>
</div>
<div class="chart1">
<canvas id="chart1"></canvas>
</div>
<div class="chart2">
<canvas id="chart2"></canvas>
</div>
<div class="chart3">
<canvas id="chart3"></canvas>
</div>
Javascript:
//example of chart variables (I only included 1 example)
var chart1 = {
labels : ["2009","2010","2011","2012","2013","2014"],
labelAlign: 'center',
datasets : [
{
label: "Visitor Average",
fillColor : "rgba(255,255,255,0)",
strokeColor : "rgba(0,34,221,1)",
pointColor : "rgba(0,34,221,1)",
pointStrokeColor : "#fff",
pointHighlightFill : "#fff",
pointHighlightStroke : "rgba(0,34,221,1)",
data: [28, 48, 40, 19, 86, 27]
},
{
label: "Baseline Average",
fillColor : "rgba(255,255,255,0)",
strokeColor : "rgba(255,0,0,1)",
pointColor : "rgba(255,0,0,1)",
pointStrokeColor : "#fff",
pointHighlightFill : "#fff",
pointHighlightStroke : "rgba(255,0,0,1)",
data: [65, 59, 80, 81, 56, 55]
}
]
}
window.onload = function(){
var ctx1 = document.getElementById("chart1").getContext("2d");
window.myLine1 = new Chart(ctx1).Line(chart1, {
responsive: true,
});
}
//change graph per drop-down selection
$(document).ready(function(){
$("select").change(function(){
$( "select option:selected").each(function(){
if($(this).attr("value")=="option-1"){
$(".chart2").hide();
$(".chart3").hide();
$(".chart1").show();
var ctx1 = document.getElementById("chart1").getContext("2d");
window.myLine1 = new Chart(ctx1).Line(chart1, {
responsive: true,
});
}
if($(this).attr("value")=="option-2"){
$(".chart2").show();
$(".chart1").hide();
$(".chart3").hide();
var ctx2 = document.getElementById("chart2").getContext("2d");
window.myLine2 = new Chart(ctx2).Line(chart2, {
responsive: true,
});
}
if($(this).attr("value")=="option-3"){
$(".chart3").show();
$(".chart1").hide();
$(".chart2").hide();
var ctx3 = document.getElementById("chart3").getContext("2d");
window.myLine3 = new Chart(ctx3).Line(chart3, {
responsive: true,
});
}
});
}).change();
});
回答4:
Taking bootstrap's tab as an example, my solution is to add class .loading-data
to .tab-content
before rendering using chart.js and remove the class after the rendering is done.
.tab-content.loading-data .tab-pane {
display: block;
}
In this way, the .tab-pane
s will display in the instant of chartjs's rendering.
来源:https://stackoverflow.com/questions/29238743/chartjs-charts-not-generating-within-tabs