求教使用云字体造成块长宽度变化的原因和解决方案。

@Humphry 提出可以用CSS把这枚跳蛋改为圆形这样的标题但我表示标题党神马的最讨厌了明明没那么长的嘛对不起我什么也没说请略过这段。

是偶尔遇到的问题,先给出代码:

<!DOCTYPE html>
<html>
<head>
<style>
@font-face{font-family:'Poiret One';font-style:normal;font-weight:400;src:local('Poiret One'),local('PoiretOne-Regular'),url(http://themes.googleusercontent.com/static/fonts/poiretone/v1/HrI4ZJpJ3Fh0wa5ofYMK8RsxEYwM7FgeyaSgU71cLG0.woff) format('woff')}
span{
    font-family: 'Poiret One';
    border:0.5em solid black;
    text-align:center;
    display: inline-block;
    padding:1em;
    background: silver;
}
</style>
</head>
<body>
<span>sf</span>
<span>segmentfault</span>
<span>segmentfault.com</span>
<span>http://segmentfault.com/u/perichr</span>
<script>
HTMLElement.prototype.__defineGetter__("currentStyle", function () { 
    return this.ownerDocument.defaultView.getComputedStyle(this, null); 
});
var list = document.querySelectorAll('li, span')
for(var i=0; i<list.length; i++){
    var item = list[i]
    item.style.height = 
    item.style.lineHeight = item.currentStyle.width
    item.style.borderRadius = (2 * item.clientWidth ) + 'px'
}

</script>
</body>
</html>

期望的结果(规整的圆):规整的圆

但在我电脑上实际如下(Fx27beta, IE11)
fx和ie的效果

chrome比较奇怪,第一次打开的时候如下
chrome第一次打开的效果
而之后F5刷新时,却能得到正确的结果。

如果使用本地字体,以上浏览器都能很好的出现正圆。

根据chrome的奇怪表现猜测可能因为云字体载入太慢致使得在js调整完高宽以后重绘了高宽,不知是否是这样。

以及为啥各浏览器表现这么奇怪。

以及如何弥补这一问题?

阅读 6.4k
1 个回答

非常厉害的程序员,永远不把“绕过问题”和“逃避问题”混为一谈。

“绕过”问题的真意,在于了解产生问题的实质,了解和尊重其他系统的实现方式,甚至于容忍其他系统本来有什么毛病,现在还有什么毛病,最后达到解决原本需求的能力——绝对不是把本来的合理需求掀翻了,然后去自说自话。


可以肯定的是:问题的原因就是如此——JS执行先于字体加载完毕。

此时在结果上,浏览器按照(退化的)默认字体进行显示。在Windows下边,我记得Fx是Arial系列字体,而Chrome是宋体等宽字体,因此都引发错误而效果并不一样。

HTML/JS端不能(也不应该)深入浏览器的缓存机制,因此也就无法对抗,也永远不要试图去对抗。


我查了一下如何直接探测font-face是否加载完成。结论是似乎没有直接的办法,现有的代码都是非常大费周章的“tricky way”(花哨手段)。例如:真的去探测文字的宽度是否有变化——亲娘哎……

你似乎值得考虑一下用成型的JS库去加载字体——只要加载字体的行为本身是JS接管的,那么在加载完成后的钩子随便怎么挂。

传送门: https://github.com/typekit/webfontloader


以下原答案犯了tricky和ass-u-me的毛病(假设浏览器对同一URL的短时重复加载是缓存的),经测试可耻的失败,千万别学。

尊重浏览器的缓存,以及网页元素的加载顺序,这样做:

  1. 如果只将URL混在CSS中,则难以通过DOM的手段确认文件是否已经妥当加载。所以考虑用尽办法,让加载这个文件的行为成为DOM的一部分。以下jsfiddle中使用了极不规范的img加载,仅为了说明这个意图,千万别学。
  2. 将JS代码封在window.onload中——window.onload不仅要求DOM就绪,更等待所有元素加载完成后才会执行,这一点区别于document.ready

如果不愿意将这个字体URL扔进DOM中,就只好采取其他办法,让JS在执行前确认浏览器已经加载完毕字体文件。

http://jsfiddle.net/m4QGa/2/

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏