老爷车IE8如何兼容图标字体


前言

首先这个标题再详细的说就是如何解决font-face在IE8下间歇性出现图标字体渲染失败的解决方案

如果你还不知道什么是图标字体,可以先阅读:链接1,链接2,链接3

先看在IE8下的问题:

正常的应该是这样:

可以看到所以图标都显示不了,最关键的是还显示出他本身的字符,这绝对是不能忍的,必须解决。

问题的原因到底是什么?

这个页面是为了快速一览图标字体的页面,这两个截图都是在IE8下截图的,所以可以看到font-face不是不兼容IE8,而是间歇性的撂挑子(出问题),至于问题原因:

猜测:是因为字体并不阻塞页面显示,当第一次打开页面,字体文件没有缓存,且字体在页面打开后才载入进来,导致浏览器没有重新渲染页面中使用图标字体的字符,才导致此问题出现的

1、搭建测试环境

首先要100%复现问题,确定bug原因。我是win8的开发机,用Oracle VM VirtualBox建立虚拟机,IE6 8 9各一台,现在用IE8测试

开发环境是Visual Studio 2013,把项目跑起来后,开启Fiddler做虚拟机的代理服务器,也就是将Fiddler作为我的真实机的反向代理服务器,这样可以让虚拟机连接我的真实机的localhost地址,来达到远程VS调试的功能。

当然你可以直接把web文件发布到局域网内网IP,然后虚拟机访问,在本例中是可以的,但是那样VS是无法使用调试(debug)功能的。

如上图所示虚拟机成功连接真实的localhost,但目前是没问题的,我们需要利用Fiddler来模拟网络问题。

 2、用Fiddler模拟网络问题

猜测是字体返回过慢,所以可以利用Fiddler的 BreakPoints ,即断点功能。

使用Fiddler-【Rules】-【Automatic Breakpoints】-【After Response】断点功能。

启用【After Response】即响应后断点,即响应已经发送至服务器,服务器也将资源返回给Fiddler,此时Fiddler等待其他资源都响应完成后,再返回给浏览器,以人为制造上述猜想的发生场景,过程如下图:

顺便一提,在本例中使用【Before Request】也是可以的,因为对于浏览器而言,使用请求前断点和响应后断点都是一样的。

接下来在虚拟机中的IE8按Ctrl+F5,强制所有资源均从新加载,在Fiddler中,把所有非字体资源文件都【Run to Completion】通过,转发给浏览器,最后再将字体转发给浏览器。

3、稳定复现

可以看到,问题又复现了:

如果此时关闭断点,再在虚拟机中清空缓存,并按Ctrl+F5 多少次,也无法复现此问题,只要使用Fiddler断点,就可以100%复现此问题。

问题找到了,就是后加载进来无法渲染,想办法强制触发渲染就可以了。

如何解决此问题?

解决方案1:加载完成后调整下浏览器大小

大家应该都知道,调整浏览器大小会造成网页重绘回流,会让页面几乎所有元素重新绘制。因为重新绘制样式了,所以自然也就可以了解决图标字体渲染失败的问题。

缺点自然不用说,这等于没解决,不过我们可以利用重绘后图标生效的特性来让强制令其页面回流重绘。

关于不知道什么是回流与重绘的同学可以参考:链接1、链接2

解决方案2:强制回流

既然我们要让所有图标字体都生效,那么最好的办法是让页面发生大面积回流,以触发整页重新渲染。那么我们就需要先知道有哪些操作可以导致回流:

以下操作会触发回流:

1、调整窗口大小

2、改变字体

3、增加或者移除样式表

4、内容变化,比如用户在input框中输入文字

5、激活 CSS 伪类,比如 :hover (IE 中为兄弟结点伪类的激活

6、操作 class 属性

7、脚本操作 DOM

8、计算 offsetWidth 和 offsetHeight 属性

9、设置 style 属性的值

10、其他会导致回流的操作

所以就有了以下失败的尝试:

给body顶部插入元素,设定body的宽度,设定绝对定位再设置回来,强制修改font-face的字体再改回来等等,最终解决是没成功,如果各位有什么好办法欢迎留言。

解决方案3:重设font-face的伪类内容

最终通过设置 !important 来覆盖所有font-face的css,再重新应用就可以实现了。

代码:


原理就是先让所有图标字体失效,再让他们生效,就可以了。

需要注意的一点是:必须注册为 window 的 onload 事件。

最常用的$.ready()是不行的,他的本质是 DOMContentLoaded ,他会在DOM加载结束后就触发,我们需要在图标字体文件加载后再执行,因为没有字体加载后的load事件(如果有请一定告诉我),所以必须要在整页所有资源都加载完成后再触发本函数。

顺便附上jquery中的 $.ready 实现方法源码:

//jquery-1.9.1.js #885
jQuery.ready.promise = function( obj ) {
    if ( !readyList ) {

        readyList = jQuery.Deferred();

        // Catch cases where $(document).ready() is called after the browser event has already occurred.
        // we once tried to use readyState "interactive" here, but it caused issues like the one
        // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
        if ( document.readyState === "complete" ) {
            // Handle it asynchronously to allow scripts the opportunity to delay ready
            setTimeout( jQuery.ready );

        // Standards-based browsers support DOMContentLoaded
        } else if ( document.addEventListener ) {
            // Use the handy event callback
            document.addEventListener( "DOMContentLoaded", completed, false );

            // A fallback to window.onload, that will always work
            window.addEventListener( "load", completed, false );

        // If IE event model is used
        } else {
            // Ensure firing before onload, maybe late but safe also for iframes
            document.attachEvent( "onreadystatechange", completed );

            // A fallback to window.onload, that will always work
            window.attachEvent( "onload", completed );

            // If IE and not a frame
            // continually check to see if the document is ready
            var top = false;

            try {
                top = window.frameElement == null && document.documentElement;
            } catch(e) {}

            if ( top && top.doScroll ) {
                (function doScrollCheck() {
                    if ( !jQuery.isReady ) {

                        try {
                            // Use the trick by Diego Perini
                            // http://javascript.nwbox.com/IEContentLoaded/
                            top.doScroll("left");
                        } catch(e) {
                            return setTimeout( doScrollCheck, 50 );
                        }

                        // detach all dom ready events
                        detach();

                        // and execute any waiting functions
                        jQuery.ready();
                    }
                })();
            }
        }
    }
    return readyList.promise( obj );
};
$.ready的源码

其他问题

1、IE8下图标字体非常有锯齿感怎么解决?如图

解决方案:做一张1*1大小的透明PNG图,然后给所有的图标字体的类样式增加IE特有的 filter 属性,如下:

filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/IEfix.png, sizingMethod=crop);
zoom: 1;

增加滤镜后效果:

图标看上去还算说得过去,至于文字的锯齿,不是本文探讨的范围内,辣个要去电脑中设置了。

缺点就是会降低页面打开速度,增加页面CPU占用量,会让页面变卡,当然前提是你页面使用了太多图标字体。

2、待补充…

代码下载

本文代码已托管至Github:

https://github.com/xxcanghai/cnblogsFiles/tree/master/fix-IE-bootstrap-font-face

扩展阅读

1、页面重绘和回流以及优化

2、回流与重绘:CSS性能让JavaScript变慢?

3、IE8 CSS @font-face fonts only working for :before content on over and sometimes on refresh/hard refresh

4、再不知道字体图标就落伍啦!

5、Font Awesome

(完)

原文地址-http://www.cnblogs.com/xxcanghai/p/5000984.html