Google Chart loads extremely small when included in jQuery-based tabs - javascript

I am using Google's line chart almost exactly as the demo - only the data has changed - inside of this jQuery tab plugin with no modification. Maybe 50% of the time, the chart will load at 400x200 even though it has been specified to load at 700x250. The containing div will have the proper width and height, but the chart as rendered by the API will load inside of that at 400x200.
I suspect this is because the tabs aren't being displayed when the API tries to render. Because of that, it tries to render in something it considers null and therefore forces itself into the smallest default resolution.
My thought is that if the display of the chart can be delayed until the appropriate tab is clicked, it would resolve the problem. Sadly, I have no idea how to do that, and my research hasn't been fruitful. The closest I could find is this thread, but I didn't find any real answers there.
I'd appreciate any advice if you have any, and I'd be glad to follow up with more information if necessary.

Rendering charts in a hidden div (which is what the non-selected tabs of a tab UI most likely are) messes with the Visualization API's ability to detect dimensions, so you want to do one of two things: either render all charts before instantiating tabs, or (as you've caught on to) bind event listeners to draw the charts when a tab is first opened. Setting the height and width in the chart's options is insufficient to solve the problem in all browsers.
I scanned over the easytabs documentation, and it looks like you should be able to do something like this:
// draw chart(s) in your default open tab
// track which tabs you've drawn charts in
var chartsDrawn = {
tab1: true,
tab2: false,
tab3: false
// etc
};
$('#tab-container').bind('easytabs:after', function (e) {
if (e.tab == 'tab-2' && !chartsDrawn.tab2) {
// draw chart(s) in tab 2
chartsDrawn.tab2 = true;
}
else if (e.tab == 'tab-3' && !chartsDrawn.tab3) {
// draw chart(s) in tab 3
chartsDrawn.tab3 = true;
}
// etc
});

change chart options to set the width and height as you need
var options = {
title: 'Company Performance'
,width:900
,height:500
};

This is how I solved using angular-bootstrap https://angular-ui.github.io/bootstrap/
<div class="google-chart" google-chart chart="chartObject1" on-ready="displayGoogleCharts()"></div>
<tab heading="Past Week" select="googleChartSizeFix()">
googleChartSizeFix = function() {
$('svg').parent().css({ opacity:"0" });
$(window).resize();
};
displayGoogleCharts = function() {
$('svg').parent().css({ opacity:"1" });
};
Each time a Tab is selected (the function googleChartSizeFix is triggered) the Google Chart is set to transparent (opacity = 0, so it does not disappear by the use of hide(), but keeps its size since its content is transparent) followed by the window resize is triggered, this forces Google Chart to fit the div that contains it, by the use of width 100% and height 100%:
"options": {
"chartArea": {
"width":'100%',
"height":'100%'
}
}
and finally once the Google Chart is ready (after resize) the displayGoogleCharts function is triggered and the opacity of the google chart is reset to 1, so the content is visible once again.

I stumbled across this "feature" of Bootstrap tabs. When cut-and-pasting multiple tabs in my HTML, I accidentally left the <div class=" tab-pane active"> in the "active" state for all the tabs. The result was that the content for all the tabs displayed sequentially in the first tab, but went away as you switched tabs.
My solution to the hidden tabs is to define them as active and then remove the "active" class from the div after I call chart.draw.
<div class="tab-pane active" id="myid" role="tabpanel">
<script type="text/javascript">
// all the chart stuff
chart.draw(data, options);
$('#myid').removeClass('active');
</script>
</div>
I see that jQuery tabs also use the "active" class. Perhaps this trick will work there too.

I solved this by, leaving off the bootstrap class in the element holding the chart, and then after chart had been loaded, then apply the bootstrap class.
For example lets say we want to setup a collapsible with the chart in it:
<a href="#div-id" data-toggle="collapse">
Expand
</a>
<div id="div-id" class="future-collapse">
<div id="some-chart"></div>
</div>
And then in your script:
/**
* Callback function E.G. google.charts.setOnLoadCallback(drawChart);
*/
function drawChart(){
// Drawing the charts
draw_some_chart();
// Applying the collapse class to our elements with the future-collapse class
$('.future-collapse').attr('class', 'collapse');
}
function draw_some_chart(){
// Draw your charts
}

Related

How can I dynamically set zoom in leaflet when in an iframe?

I have an asp.net app with an iframe in it on a tab in the markup. The content of the iframe is a Leaflet based map display. The markup:
<div id="tabMapDisplay" class="tab-pane row container">
<div id="divMapDisplay" runat="server" class="col-xs-12 col-sm-12 col-md-12 col-lg-12 fieldset-border-bg-defaults">
<div class="embed-responsive embed-responsive-16by9">
<iframe id="ifrMap" runat="server" allowfullscreen="true" class="embed-responsive-item" />
</div>
</div>
</div>
The source url for the Leaflet display is a parameterized url set in the code behind based on data for the overall display. Works great, shows the map with the target as desired. Except if the target is a very large feature on the map it then shows the display and the target feature can stretch beyond the boundary visible in the iframe so that part of the feature is hidden until the user zooms out on the map.
I decided to try having the Leaflet app adjust the zoom level dynamically so that larger features will fit when viewed in an iframe. When viewed straight up in the browser they show just fine. Here's what I've got trying to determine the zoom level so far (I've cut out all the experimental bits I've been attempting all day):
function inIframe() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
function optimalZoomLevel(boundsZoom) {
if (boundsZoom < zl) {
zl = boundsZoom;
}
if (inIframe()) {
zl = zl - 1;
}
return zl;
}
zl is set to a default zoom level value as a starting point. Everything is fine as I said when directly in the browser. But in the iframe (the detection function inIframe works correctly), once this code knows it the page is in an iframe no matter what I do I cannot seem to get the height and width of the containing frame. Everything I've tried comes up either 0, undefined, or the dimensions of the browser window not the iframe. I've tried using a number of different options I've found by searching but so far nothing more than 0 or undefined for the width value I'm looking for. I figure if I can know the width then I can adjust the zoom level to something appropriate so the whole of the larger candidate marker/shapes show in the iframe.
Any suggestions are appreciated.

Make Panels invisible amchart

I would like to make panels visible/invisible by button.
in this sample remove/add panels.
But when adding, I need to make a panel and settings again.
I would like to just make panels visible/invisible not delete.
I have googled around and not found samples.
Is it possible??
Thanks to #Robbert reply
I could hide the panel . like this .
$(".amcharts-stock-panel-div-stockPanel1").hide();
However it does not re-adjust the each panel size.
If I call the
So I try like this .
$(".amcharts-stock-panel-div-stockPanel1").hide();
chart.panels[1].percentHeight = 1;
chart.validateNow();
it hide the panel and adjust the each panel height.
However, if you use validateNow() when percentHeight = 1;
this error happens.
amcharts.js:26 Uncaught TypeError: Cannot read property 'translate' of undefined
at b.fixVLine (amcharts.js:26)
at b.adjustBalloonCoordinate (serial.js:17)
at b.showBalloon (amcharts.js:5)
at b.handleCursorMove (serial.js:8)
at b.dispatchMovedEvent (amcharts.js:27)
at b.syncWithCursorReal (amcharts.js:28)
at b.syncWithCursor (amcharts.js:28)
at b.handleCursorChange (amstock.js:2)
at b.a.inherits.b.fire (amcharts.js:1)
at b.dispatchMovedEvent (amcharts.js:27)
my final solution is like this , not use css, but prepare variable panelBack for panel backup.
//removing ...
pos = //panel position.
var panelBack = chart.panels[pos];
chart.removePanel(chart.panels[pos]);
chart.validateNow();
//adding...
chart.addPanelAt(panelBack,1);
chart.validateNow();
By looking at the source of the demo, you'll see that the second Stock Panel gets a classname of amcharts-stock-panel-div-stockPanel1. You could hide it using CSS:
.amcharts-stock-panel-div-stockPanel1 {
display: none;
}
.amcharts-stock-panel-div-stockPanel1 * {
/* hide SVG nodes as well */
visibility: hidden;
}
However, amCharts itself is not aware that this panel is hidden, so it will not re-adjust the height of the first stock panel when "removing" it.
I would advice following the method as seen in the example.

Cannot implement dynamic height jQuery Wookmark

I have some divs that have dynamic heights controlled by a 'click' function as below:
$('.expand').click(function() {
$(this).next('.collapse').slideToggle();
});
I am attempting to apply the jQuery wookmark plugin to the divs, and it works, apart from when their heights are dynamically resized by expanding one of the sections. From the documentation, I copied over one of the examples to my code, and the dynamic height works
$(document).ready(new function() {
// Prepare layout options.
var options = {
autoResize: true, // This will auto-update the layout when the browser window is resized.
container: $('#container'), // Optional, used for some extra CSS styling
offset: 30, // Optional, the distance between grid items
itemWidth: 300 // Optional, the width of a grid item
};
// Get a reference to your grid items.
var handler = $('.outerwrapper');
// Call the layout function.
handler.wookmark(options);
// Capture clicks on grid items.
handler.click(function(){
// Randomize the height of the clicked item.
var newHeight = $('img', this).height() + Math.round(Math.random()*300+30);
$(this).css('height', newHeight+'px');
// Update the layout.
handler.wookmark();
});
});
You can see this working here. How can I make it so that when you click one of the headings inside the divs, the layout updates, as it does in the example. Thanks in advance.
Usually third party jQuery plugins include some kind of "Refresh" or "Resize" function.
Taking a quick look at the function, it doesn't appear to have one; however, since there is an "autoResize" option (which will reload the layout on browser resize), you could simply create a click event that triggers the "resize" event like so:
JAVASCRIPT:
$("h1.resize").live("click", function()
{
$(window).trigger('resize');
});
http://api.jquery.com/trigger/
http://api.jquery.com/resize/
EDIT:
Re-reading the question again,
Looks like this:
handler.wookmark();
should refresh the layout (based on your posted code). So you should be able to use that instead of the resize trigger.
$("h1.resize").live("click", function()
{
handler.wookmark();
});

How to enable and disable a DIV

I am using the Prototype JavaScript library and have this div:
new Element('div', {id: 'summaryGraph', style: 'width: 100%; height:90px;'});
Inside that div, I am showing a graph:
summaryGraph: function (data, bounds) {
var p = Flotr.draw(
$('summaryGraph'),
[data],
{
}
);
return p;
}
Now my question is, is it possible to disable and enable the div depending on the conditions?
If you want to "disable" the Graph you have 2 possibilities:
hide the Graph and show a placeholder (image, text) stating that the Graph is not accessible at the moment
overlay the Graph with a semi-transparent DIV, which will prevent any interaction with it
I'm not sure what you mean by 'disabling' the graph. But you may want to take a look at hide() (relevant docs) or show() (relevant docs).
If your graph should not respond to mouse events, add a disable flag and in the mouse event listener, return false if the flag is set.

Flot graph does not render when parent container is hidden

I was having an issue where a flot graph would not render in a tabbed interface because the placeholder divs were children of divs with 'display: none'. The axes would be displayed, but no graph content.
I wrote the javascript function below as a wrapper for the plot function in order to solve this issue. It might be useful for others doing something similar.
function safePlot(placeholderDiv, data, options){
// Move the graph place holder to the hidden loader
// div to render
var parentContainer = placeholderDiv.parent();
$('#graphLoaderDiv').append(placeholderDiv);
// Render the graph
$.plot(placeholderDiv, data, options);
// Move the graph back to it's original parent
// container
parentContainer.append(placeholderDiv);
}
Here is the CSS for the graph loader div which can be placed
anywhere on the page.
#graphLoaderDiv{
visibility: hidden;
position: absolute;
top: 0px;
left: 0px;
width: 500px;
height: 150px;
}
Perhaps this is better solution. It can be used as a drop in replacement for $.plot():
var fplot = function(e,data,options){
var jqParent, jqHidden;
if (e.offsetWidth <=0 || e.offetHeight <=0){
// lets attempt to compensate for an ancestor with display:none
jqParent = $(e).parent();
jqHidden = $("<div style='visibility:hidden'></div>");
$('body').append(jqHidden);
jqHidden.append(e);
}
var plot=$.plot(e,data,options);
// if we moved it above, lets put it back
if (jqParent){
jqParent.append(e);
jqHidden.remove();
}
return plot;
};
Then just take your call to $.plot() and change it to fplot()
The only thing that works without any CSS trick is to load the plot 1 second after like this:
$('#myTab a[href="#tabname"]').on("click", function() {
setTimeout(function() {
$.plot($(divChartArea), data, options);
}, 1000);
});
or for older jquery
$('#myTab a[href="#tabname"]').click (function() {
setTimeout(function() {
$.plot($(divChartArea), data, options);
}, 1000);
});
The above example is applied to Bootstrap tags for Click funtion. But should work for any hidden div or object.
Working example: http://topg.org/server-desteria-factions-levels-classes-tokens-id388539
Just click the "Players" tab and you'll see the above example in action.
This one is a FAQ:
Your #graphLoaderDiv must have a width and height, and unfortunately, invisible divs do not have them. Instead, make it visible, but set its left to -10000px. Then once you are ready to show it, just set it's left to 0px (or whatever).
OK, I understand better now what you're actually saying... I still think your answer is too complicated though. I just tried this out using a tabbed interface where the graph is in a hidden tab when it's loaded. It seems to work fine for me.
http://jsfiddle.net/ryleyb/dB8UZ/
I didn't have the visibility:hidden bit in there, but it didn't seem necessary...
You could also have visibility:hidden set and then change the tabs code to something like this:
$('#tabs').tabs({
show: function(e,ui){
if (ui.index != 2) { return; }
$('#graphLoaderDiv').css('visibility','visible');
}
});
But given the information provided, none of that seems particularly necessary.
I know this is a bit old but you can also try using the Resize plugin for Flot.
http://benalman.com/projects/jquery-resize-plugin/
It is not perfect because you'll sometimes get a flash of the non-sized graph which may be shrunk. Also some formatting and positioning may be off depending on the type of graph that you are using.

Categories

Resources