With the release of Internet Explorer 9 on Monday (Mar 14th), I've been going through my various websites to ensure everything is working as expected in the new browser. For the most part, I have seen no problems and, in fact, I have only seen benefits of new CSS3 and HTML5 standards in IE9 (rounded corners, woohoo!). But, this post is dedicated to the only issue I've found so far... the "bgiframe" plugin for jQuery.
The Perfect Storm
There's a specific scenario in which this plugin will fail:
- Windows Vista (Windows NT 6.0) or Windows Media Center in Windows 7 (Media Center PC 6.0)
- Internet Explorer 9
- Document rendering in Standards Mode (as opposed to Quirks Mode)
Got it? Good.
Why does it fail?
IE9 on these versions of Windows has the following User Agent (or very similar):
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; MS-RTC LM 8 )
The part we're interested in is the Windows versions, "Windows NT 6.0" and "Media Center PC 6.0". Clearly, these are the OS version, not the browser version. Unfortunately, the bgiframe plugin's browser version check is a bit too rudimentary to realize this. Here's the code from the most recent version (June 2007) of this plugin:
if ( $.browser.msie && /6.0/.test(navigator.userAgent) ) { /* ...other stuff here... */ }
This RegEx test looks for the existence of "6.0" anywhere in the UA string. As you saw above, we have "6.0" in there twice, even though I'm using IE9.
So, we need to fix this little flub to make it very specific to our needs:
if ( $.browser.msie && parseInt($.browser.version) === 6 ) { /* ...other stuff here... */ }
In this way, we utilize jQuery's browser version sniffer (which has the added benefit of jQuery upgrades, if the sniffer ever gets updated) and we also ensure that we're targeting IE6 directly, not a random instance of "6.0" from any number of OS's or plugins which add to the UA string. After all, that's what this plugin is designed to do -- fix the IE6 z-index bug.
The other component of this is "Standards Mode" rendering. This bug didn't come to light for me until now because the website I work on renders in Quirks Mode (don't get me started on this!), so the Javascript engine was more forgiving. When in Standards Mode, IE9 yells about this line in the plugin:
this.insertBefore( document.createElement(html), this.firstChild );
I haven't taken the time to understand why, but it doesn't like "this.insertBefore()" in this use. But, that's for another day.
Thanks, this was really helpful. As for the part IE is complaining about, check out this:
http://stackoverflow.com/questions/5344029/invalid-character-dom-exception-in-ie9
This was driving me insane. Excellent post. Many thanks.
This save me, after 20 very hard minutes of research. Really thank you, Rob.
Hey!
Try to modify that line with :
this.insertBefore( document.createElement('html'), this.firstChild );Works better now ?
Thanks so much for this explanation! It’s always good to understand what’s happening. But… why not use conditional comments to only load bgiframe for IE6?
@Lora, @Chuck, @deste: Thanks! It’s great to know that this was helpful.
@Nico: You’re right – that is the correct way to use createElement(). But, the plugin is only necessary for IE6, so that code should not be executing (and taking processing time) at all in other browsers. Performance is king!
@Lauren: You can totally do that. If you have it as a standalone file, conditional comments will work just as well. My projects tend to have a single plugins.js file (containing all global plugins) for simplicity. Either way is valid — it’s just up to your preference.
Was also useful me.
Why the library was not updated with this change?
With this change run in ie9 but don’t run in ie8.
Works on both version with this.insertBefore(document.createElement(($.browser.msie && $.browser.version==’9.0′) ? ‘html’ : html), this.firstChild)
We were looking for this same issue. Single quotes breaks compatibility for ie6, however if you just change the variable name html to html_new, this solved all of our issues. So give that one a try.
(function($){$.fn.bgIframe=$.fn.bgiframe=function(s){if($.browser.msie&&parseInt($.browser.version)===6){s=$.extend({top:'auto',left:'auto',width:'auto',height:'auto',opacity:true,src:'javascript:false;'},s||{});var prop=function(n){return n&&n.constructor==Number?n+'px':n;},new_html='<iframe class="bgiframe"frameborder="0"tabindex="-1"src="'+s.src+'"'+'style="display:block;position:absolute;z-index:-1;'+(s.opacity!==false?'filter:Alpha(Opacity=\'0\');':'')+'top:'+(s.top=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+\'px\')':prop(s.top))+';'+'left:'+(s.left=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+\'px\')':prop(s.left))+';'+'width:'+(s.width=='auto'?'expression(this.parentNode.offsetWidth+\'px\')':prop(s.width))+';'+'height:'+(s.height=='auto'?'expression(this.parentNode.offsetHeight+\'px\')':prop(s.height))+';'+'"/>';return this.each(function(){if($('> iframe.bgiframe',this).length==0)this.insertBefore(document.createElement(new_html),this.firstChild);});}return this;};})(jQuery);