Flash and the Firefox Reframe Problem Thursday, May 22, 2008

So I spent the last two days trying to figure out why Firefox insists on reloading flash content whenever I flip around in my tasty javascript-y tabbed interface.

You haven't seen this? I'm not surprised, it really only occurs if you're embedding flash into a web page whose layout is being managed by javascript. Some examples of UI libraries like this are Scriptaculous, Ext-JS, and my latest BSO, jQuery.

All of these libraries modify the style of the flash object's parent <div> in ways (usually, display:none, but position:absolute will do it, too) that somehow goads Firefox into helpfully reloading the Flash from scratch. Reportedly it's not just swfobjects -- any generic <object> or <embed>, including Java applets, will get reloaded.

For flash charting components (we're playing with amCharts at my company, Pharos), this problem is multiplied by the fact that the flash application will re-download whatever historical data you're trying to present, delaying the presentation and using up more bandwidth. (Hey, how's your ETag support looking?)

It actually took me about two hours to find what the root problem is, and you're not going to believe it:

https://bugzilla.mozilla.org/show_bug.cgi?id=90268

This bug has been open since July 2001! That's Firefox 0.9! Holy cripey!

Worse, it's still not fixed, even in the brand-spanking-new Firefox 3.

The good news is, there's a relatively easy way to get around this, if your JS library is using CSS for hiding elements. What I mean by that is, the javascript code is hiding elements by adding a class to them (in Ext-JS, this class name defaults to .x-hide-display), and is not setting display:none directly on your DOM elements. (You'll probably need to look at the implementation of hide() and show() for your specific library to know for sure.)

So if hiding DOM elements is done via style classes, the low-hanging fruit is to redefine the CSS rule to look like this:

.x-hide-display {
    display:block!important; /* overrides the display:none in the original rule */
    height:0!important;
    width:0!important;
    border:none!important;
    visibility:hidden!important;
}

(this is exactly what I did to make my flash charts work in Ext-JS).

You can probably override the hide() and show() functions in your particular library to do something like this, as well. YMMV.

Now, you're saying to yourself, "Dude, you must be breaking something else that used to depend on the display:none behavior." Well, you're probably right, but I haven't found it yet. If you know, or if you find out, let me know in the comments.

5 comments:

DaveC said...

Hi Mike, just wondering if you have and update on this issue... I've run in to it on my site as well, for example.

http://mrdavec.com/games/bubblebobble/

And not just Firefox, but Google Chrome does it as well.

dandante said...

Has anyone implemented this fix for jQuery? It isn't immediately obvious to me how to override the hide() and show() methods to fix this....

Anonymous said...

For JQuery, override the following CSS style:

.ui-tabs .ui-tabs-hide

raul said...

In jQuery UI Tabs this breaks the fact that hidden tabs should not be in the DOM layout, and so you get a massive scrollbar (since it's counting the layout for the elements which aren't visible in other tabs).

David said...

http://stackoverflow.com/questions/1224228/firefox-hide-embeded-object-bug-workaround

.hideme {
padding: 0 !important;
margin: 0 !important;
display: block !important;
height: 0 !important;
width: 0 !important;
border: none !important;
visibility: hidden !important;
}

this solution works without the scrollbar side effects like raul found.