IE8 web font iframe bug workarounds
Asked Answered
A

9

29

This blog post (slightly annoying page there) (and that's not my blog by the way) describes a bizarre bug I ran into yesterday in Internet Explorer 8 only. The bug involves .EOT web fonts and <iframe> elements.

I haven't extensively researched the exact trigger for the bug, but it's basically the case that a page using a web font that loads content into an <iframe> such that the frame also uses a web font becomes "defaced" by the browser. The previously-OK text rendered with the web font suddenly shifts to awful-looking Arial or something else, sort-of on its own. Sometimes it flips back, only to degrade again on random user interactions like mouse moves.

That blog post has an example. To clarify, it's the containing page that gets messed up, not the page in the <iframe> (at least, that's the case so far in my experience).

Has anybody found a better workaround than what's suggested in that blog, which is to force a "reload" of the CSS <link> element from whence the @font-face declarations come? (I could do that but it'd be a minor pain, plus it would force me to move my font setup out of my document <head> which if I recall is a performance issue; I'll have to scrounge around and find that tidbit again.)

editupdate

OK here's a test page. Here's the main (container) page:

<!DOCTYPE html>
<html>
  <head>
    <style id='font_style'>
      @font-face {
        font-family: 'bold-display';
        src: url('DejaVuSans-Bold.eot');
      }
    </style>
    <style>
      .fancy { font-family: bold-display, "franklin gothic medium", "verdana", sans-serif; font-size: 32px; }
      iframe { width: 500px; height: 200px; }
      #floater {
        position: absolute;
        top: 100px; left: 100px;
        display: none;
      }
      #floater.showing {
        display: block;
      }
    </style>
    <script>
      function load() {
        var frame = document.createElement('iframe'),
          floater = document.getElementById('floater'),
          target = document.getElementById('target');

        frame.src = 'frame.html';
        target.appendChild(frame);
        floater.className += 'showing';
      }
      function unload() {
        var floater = document.getElementById('floater'),
          target = document.getElementById('target');
        target.innerHTML = '';
        floater.className = floater.className.replace(/\bshowing\b/g, '');
      }
    </script>
  </head>
  <body>
    <div class='fancy'>Hello World</div>
    <button type='button' onclick='load()'>Click Me</button>
    <div id='floater'>
      <div id='target'></div>
      <button type='button' onclick='unload()'>Close</button>
  </body>
</html>

The frame page has the same @font-face and a dummy message.

The problem appears to have something to do with using the loaded fonts with a list of more than one alternate font. I (for no good reason) had tossed in a couple of similar more-common fonts in my "font-family" values. When I dropped them back to:

 .title { font-family: bold-display, sans-serif; }

then the problem went away (or at least it seems to have gone away so far).

Thanks to those who've helped out. To @albert, add an answer summarizing what you tried and I'll upvote you :-)

Agraphia answered 14/10, 2011 at 13:5 Comment(9)
where's your code? did you use exactly the same code as that post?Greenheart
Well it's an iframe that loads a page that's got similar CSS to the parent page, including the @font-face stuff. I could try to make a sample, but it's tricky because the bug is, well, a bug. (There's really no doubt that this is a browser bug; it's totally obvious if you see it.)Agraphia
i can't duplicate it. the iframe has xhtml transitional doctype, the post has html5, the post says you need the same doctypes...the post styled text is cabin, the @font-face in the iframe references saxMonoRegular, etc., etc., etc. happy to help, just don't see it at allGreenheart
OK, I'll see if I can make a test. Shouldn't be too hard.Agraphia
Hmm ... you're right; it looks like there's more to this than I thought (and possibly more than that blog post described). I know the problem is happening to me, and it's the sort of effect that is simply impossible to do even if you wanted to (it draws the bogus "replacement" font with the same metrics as the "lost" font, which is pretty weird), but maybe there's other stuff about my page that's contributing to the problem.Agraphia
Hmm, that is odd. On your test page, clicking Click Me then Close does nothing, but clicking Click Me twice hides it on the page and it causes it to loose the font declaration. I'll look into this more.Blackburn
It's freaky weird. Try "Click Me" - "Close" and then resize the window a few times.Agraphia
Wow, it's even nastier than that. If you click Click Me, and the entire <iframe> is not within the browser window, it will not load the font in the <iframe> and it will unload it on reload. It obviously has to do with how they paint the page regarding <iframe>'sBlackburn
Hope. Clicking click me again after the font has been unloaded seems to cause the page to reload the font into the dom. There might be a solution buried in that fact.Blackburn
B
25

So, there is no performance hit with the following (as long as your CSS is reasonably sized), you get to leave the <link> tag in <head>, and it works without issue, but you are still basically 'reloading' your <link> elements (though you are not doing so by resetting their url).

On the removal of the <iframe> element, simply do the following:

var sheets = document.styleSheets;
for(var s = 0, slen = sheets.length; s < slen; s++) {
    sheets[s].disabled = true;
    sheets[s].disabled = false;
}

Reloading is all I can really think of as working since it seems to be removing it in garbage collection from the <iframe>. Set up a test page that obviously only works for IE 8.

Of Note: I was originally unable to reprodcue this issue using Google web fonts, and had to specifically download a .eot font for use for this. So your work around maybe to use WOFF fonts first, and only load EOT if necessary.

Not exactly sure if this is what you were looking for, but if it's not, please clarify and I'll edit as necessary.

Update 1: The cause

So, I've narrowed down the cause of the issue. I am only able to reproduce if the following occurs (and this is a nasty one folks).

  • And <iframe> is contained within a parent element
  • The parent element's class is changed
  • The class does not change the display of the element it is being applied to (or really, if it does not change the overall display on the <iframe> element)

And, from what I can tell, yes, it has to be the class name. I was unable to reproduce given the same steps on the id of an element. Seriously. This one is a nasty one. I'll keep digging a bit.

Update 2: A Secondary Cause

If the <iframe> is not fully in the browser window on draw, it will not load the font within the <iframe> window, and will unload the font for the main window whenever it has to redraw the page (most notably when it is resized). This is a gnarly bug.

Update 3: What should be a solution

Instead of using display: none;, set the height and the width of the element to be 0px and the overflow: hidden;, and you'll get the same effect as display none, but it will not remove it from the DOM, thereby not redrawing the page and taking your font. My test page from before has been updated, so you can see the effect on what used to be the bug.

Blackburn answered 25/10, 2011 at 4:34 Comment(7)
Holy cow I'm going to try this immediately.Agraphia
This definitely seemed to partially work, but if you note my edit to the question (based on stuff @Greenheart prodded me to figure out) the problems went deeper than just this. I'll accept however because you were nice enough to devote the time.Agraphia
@Agraphia Update 1 posted. Found the cause of the issue. Investigating. Also, my fix for sweeping through the stylesheets still seems to work at the moment, but I'll see if I can't find a better cure.Blackburn
@Agraphia And I've updated with what should be a working solution. If it doesn't work for you, let me know.Blackburn
Haha, well, glad I could help. If it's worth the bounty, you can always award me that ;)Blackburn
Oh shoot I thought I already had (first time I did a bounty). Sorry, I clicked it now.Agraphia
Even though this didn't work for us - see my answer below - it gave me a good direction to investigate. So, thanks!Viole
G
2

If you just generated cross-browser @font-face syntax via fontsquirrel.com, I don't think you'd even have an issue. you want to be embedding:

@font-face{
    font-family: 'DejaVuSansBook';
    src: url('DejaVuSans-webfont.eot'); /* ie9 compat mode */
    src: url('DejaVuSans-webfont.eot?#iefix') format('eot'),  /* ie 6-7-8 */
         url('DejaVuSans-webfont.woff') format('woff'), /* modern browsers */
         url('DejaVuSans-webfont.ttf') format('truetype'), /* Safari, Android, iOS */
         url('DejaVuSans-webfont.svg#webfontLXhJZR1n') format('svg'); /* Legacy iOS */
}

you could also use the webfont loader script if you find the syntax failing. I didn't compare functionality to the code answered above (I'm sure it works), this is the js typekit and googlefonts use to load their fonts.

Greenheart answered 26/10, 2011 at 1:49 Comment(5)
No, this is not true. I do in fact have exactly that sort of CSS. The issue is an IE8 bug, no doubt about it. Check the examples that have been linked in the other answers and comments.Agraphia
ok. if you did, in fact, have that css, why didn't you just leave it there? i don't understand why you'd take out the .eot's targeting ie. i'm not saying you're wrong, but ie8's pretty old....i would have thought there'd be much more documentation about such a bug. and i've not once had a problem cross-browser with that syntax. just trying to help.Greenheart
I didn't take out the .eot's targetting IE. The bug is real, but because it only happens in these somewhat unusual cases of iframes etc it doesn't actually affect that many sites. The use of web fonts plus iframes that come and go, in other words, is fairly rare.Agraphia
if you had css just like this, but didn't take it out.....i have no clue what you mean. the css in the demo only has one link in the @font-face statement, to a .eot file.Greenheart
Yes, that's right. The problem turned out to be caused by the reference to the web font. When a CSS rule has something like "font-family: the-webfont, Arial, Verdana, sans-serif;" - more than two font family names, in my experience - the bug is triggered when the iframe is loaded and unloaded. Read the extensive answer @Ktash provided - it's pretty amazing. I do thank you for your contribution of course.Agraphia
A
2

I came across this q&a while researching a very similar situation with the exception of no iframe whatsoever being involved. All it took was using a google web font and a lot of floated divs to cause IE8 to render the text differently after reload. Having read this

The problem appears to have something to do with using the loaded fonts with a list of more than one alternate font.

I changed the font-stack in my css to only a single alternative, and the bug went away.

Thanks a lot for pointing me in the right direction!

Auspice answered 25/2, 2012 at 19:21 Comment(0)
M
2

The solution I ended up using was

@font-face {
    font-family: 'NewsGothicFSMedium';
    src: url('NewsGothic-Medium-webfont.eot');
    src: url('NewsGothic-Medium-webfont.eot?#iefix')  format('embedded-opentype'),
         url('NewsGothic-Medium-webfont.woff') format('woff'),
         url('NewsGothic-Medium-webfont.ttf') format('truetype'),
         url('NewsGothic-Medium-webfont.svg#webfont') format('svg');
    font-weight: normal;
    font-style: normal;
}

@font-face {
    font-family: 'NewsGothicFSMediumIframe';
    src: url('NewsGothic-Medium-webfont.eot');
    src: url('NewsGothic-Medium-webfont.eot?#iefix')  format('embedded-opentype'),
         url('NewsGothic-Medium-webfont.woff') format('woff'),
         url('NewsGothic-Medium-webfont.ttf') format('truetype'),
         url('NewsGothic-Medium-webfont.svg#webfont') format('svg');
    font-weight: normal;
    font-style: normal;
}

Then just reference the Iframe font-family within the iframe css. Its a little extra css but I think its better then having to reload css if you are only using a few fonts.

Maidservant answered 29/4, 2013 at 22:52 Comment(3)
You probably want your SVG before your WOFF or things will look pretty bad in Chrome on Windows. Also, I was already doing something like this :-)Agraphia
Everything I have seen puts svg last. Did I miss something?Maidservant
Here is one of several recent(ish) articles on the issue.Agraphia
S
1

None of the other suggested solutions worked for us, so we ended up having to simply keep the entire page hidden until the page was loaded. We did that with the following piece of code:

<!--[if lte IE 8]>
    <style type="text/css">
        html {
            visibility: hidden;
        }
    </style>
    <script type="text/javascript">
        $(window).load(function() {
            $('html').css('visibility', 'visible');
        });
    </script>
<![endif]-->
Softshoe answered 2/5, 2012 at 20:11 Comment(1)
This may work for some page layouts, but dynamic updates after initial page loads can cause the problem to appear spontaneously!Agraphia
N
1

IE < 9 has a bug in the way it handles the src attribute of the font.

According to https://github.com/stubbornella/csslint/wiki/Bulletproof-font-face , you can avoid it by simply adding a questionmark after the first font url and trick the way the browser interprets the url. Worked like a charm for me.

Nitrobenzene answered 17/4, 2013 at 13:33 Comment(2)
Thanks. In this case I don't believe that has anything to do with it. In my real pages, I'm using that IE "protection" technique; on the test page, I only import the EOT font for IE.Agraphia
Ah, alright. I think I was a bit too quick when i read your post, sorry. I had that strange font changing behaviour in the iframe of a Facebook app. On the first page visit, it renders ok, but after a page reload it gets reverted to some default font. Quite strangely, it set the correct font outside the iframe, so Facebook got a completely new look ;) When loading the page outside the iframe, it works without the ?#iefix though.Nitrobenzene
V
1

The problem we encountered seemed to be that when an iframe with the same name was unable to be loaded (a 404 in our case), IE would (sometimes) unload the font on the main page. Our solution was to rename the font on the iframed page (in case the font can't be loaded for some reason beyond our control) and ensure that it is properly loaded normally.

Viole answered 17/4, 2013 at 16:52 Comment(0)
K
1

My solution has been to set just one possibility of typography for IE8. Example:

general.css

@font-face {
  font-family: 'patua-one'; /PatuaOneRegular';/
  src: url('../fonts/PatuaOne-Regular-webfont.eot');
  src: url('../fonts/PatuaOne-Regular-webfont.eot?#iefix') format('embedded-opentype'),
       url('../fonts/PatuaOne-Regular-webfont.woff') format('woff'),
       url('../fonts/PatuaOne-Regular-webfont.ttf') format('truetype'),
       url('../fonts/PatuaOne-Regular-webfont.svg#PatuaOneRegular') format('svg');
  font-weight: normal;
  font-style: normal;
}

body {
    font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
}

h1,h2,h3,h4,h5,h6 {
    font-family: patua-one,Arial,Helvetica Neue,Helvetica,sans-serif;
}

<!--[if IE 8]>
    <link rel="stylesheet" type="text/css" href="ie8.css">
<![endif]-->

ie8.css

body {
    font-family: Arial;
}

h1,h2,h3,h4,h5,h6 {
    font-family: patua-one;
}
Kidder answered 18/6, 2013 at 11:52 Comment(2)
I reformatted your CSS samples. See How do I format my code blocks? for more help with that. :-)Minutely
Yes, I agree - this seems to be important. Thanks for your answer!Agraphia
W
1

I had a similar problem, but it was with a simple frameset, not with dynamic iframes. Riffing on the answer from ktash, this centralizes the hack into the top level frame, without impacting all the pages that might load around the frameset.

The conditional block will override the onload handler for just IE8.

There is still a FOUC occasionally, but at least the situation corrects itself rather than requiring the user to move around.

<html>
<head>
<script>
function markFrameLoaded() { 
    // noop for the well behaved browsers of the world
}
</script>
<!--[if IE 8]>
<script>
function markFrameLoaded() {
    var frameCount = window.frames.length;
    for ( var f = 0 ; f < frameCount ; f++ ) {
        var styleCount = window.frames[f].document.styleSheets.length;
        for ( var s = 0 ; s < styleCount ; s++ ) {
            var sheet = window.frames[f].document.styleSheets[s];
            sheet.disabled = true;
            sheet.disabled = false;
        }
    }
}
</script> 
<![endif]-->
</head>
<frameset rows="*,*">
<frame src="top.html" onload="markFrameLoaded()">
<frame src="bottom.html" onload="markFrameLoaded()">
</frameset>
</html>
Which answered 10/9, 2013 at 22:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.