使用jQuery如何知道@font-face字体何时加载完成?

46

我正在使用@font-face,但我讨厌Firefox显示默认字体,等待加载@font-face字体,然后替换它。所以整个页面都会闪烁出新字体。

Webkit浏览器只有在字体加载完成之后才显示文本,看起来更加清爽。

所以,我想知道jQuery是否可以帮助我知道页面上的所有数据何时加载完成,包括@font-face文件,以便我可以显示我的文本?是否有一个jQuery方法告诉我什么时候一切都已加载完成?


1
我差点就发了一个回答,但是它太糟糕了,所以我想等等看是否有聪明的人知道一个好方法来做。 - Pointy
@Pointy - 哈哈,没问题。 - Nic Hubbard
值得一提的是,我之前中途放弃的答案是建议使用计时器来观察某些测试元素(可能是隐藏的),并等待其大小改变后再加载字体。也就是说,在尝试加载字体之前启动计时器,然后当计时器例程(每50毫秒运行一次左右)发现某个框的大小已经改变时,它便知道字体已经到达了。 - Pointy
1
哦哦哦,你可以使用Image对象加载字体文件,当onload触发时,您可以切换开关以启用CSS并显示样式化的元素。 - Pointy
...除非响应不是图像,否则"load"事件可能不会触发... - Pointy
如果有人仍然在这里,我从这个链接中选择了Option1,它对我很有效! https://portalzine.de/dev/options-to-detect-when-a-font-face-has-been-loaded/ - chris
7个回答

54

我使用这个函数-在Safari,Chrome,Firefox,Opera,IE7,IE8和IE9中进行了测试:

function waitForWebfonts(fonts, callback) {
    var loadedFonts = 0;
    for(var i = 0, l = fonts.length; i < l; ++i) {
        (function(font) {
            var node = document.createElement('span');
            // Characters that vary significantly among different fonts
            node.innerHTML = 'giItT1WQy@!-/#';
            // Visible - so we can measure it - but not on the screen
            node.style.position      = 'absolute';
            node.style.left          = '-10000px';
            node.style.top           = '-10000px';
            // Large font size makes even subtle changes obvious
            node.style.fontSize      = '300px';
            // Reset any font properties
            node.style.fontFamily    = 'sans-serif';
            node.style.fontVariant   = 'normal';
            node.style.fontStyle     = 'normal';
            node.style.fontWeight    = 'normal';
            node.style.letterSpacing = '0';
            document.body.appendChild(node);

            // Remember width with no applied web font
            var width = node.offsetWidth;

            node.style.fontFamily = font + ', sans-serif';

            var interval;
            function checkFont() {
                // Compare current width with original width
                if(node && node.offsetWidth != width) {
                    ++loadedFonts;
                    node.parentNode.removeChild(node);
                    node = null;
                }

                // If all fonts have been loaded
                if(loadedFonts >= fonts.length) {
                    if(interval) {
                        clearInterval(interval);
                    }
                    if(loadedFonts == fonts.length) {
                        callback();
                        return true;
                    }
                }
            };

            if(!checkFont()) {
                interval = setInterval(checkFont, 50);
            }
        })(fonts[i]);
    }
};

使用方法如下:

waitForWebfonts(['MyFont1', 'MyFont2'], function() {
    // Will be called as soon as ALL specified fonts are available
});

3
优秀且简洁的解决方案。有没有遇到过字体不会改变大小(从而锁定!也许设置一个最大等待时间会更明智..)的情况? - T4NK3R
3
创建每个字体的 setInterval() 稍微有些不太幸运。更希望有一个单独的 setInterval() 来检查所有仍需要检查的字体。 - jfriend00
2
不适用于Chrome 39。当回调被调用时,字体已经加载,但我的页面上仍有许多元素以备选字体(Arial)呈现。 - Jonas Sourlier
1
我发现这个解决方案存在一个严重的 bug,基本上有时候不起作用,因为一旦你改变了尚未加载的字体的 fontFamily,它就开始使用继承的字体(由于任何 CSS 规则而导致的 span),所以它会说已经改变了,因此在 Web 字体加载之前执行回调。我刚刚提交了一个修复。 - Ivan Castellanos
1
这段代码有问题;它会多次运行回调函数,因为你为每个字体设置了间隔 - 但只有在所有字体都加载完成时,它们才会被清除。将 if(interval) { clearInterval(interval);} 移动到 ++loadedFonts; 后面可以解决这个问题。 - 1owk3y

27

好的,这很简单。我只需要把我的文本设置为:

a.main {visibility: hidden;}

然后添加:

$(window).bind("load", function() {
       $('#nav a.main').addClass('shown');
});

然后确保以下内容也在我的CSS文件中:

a.main.shown {visibility: visible;}

1
@Chris - 我们现在真的很担心用户关闭 JS 吗?你还活在 90 年代吗?我的目标用户不会关闭 JS。 - Nic Hubbard
3
在我的网站上,如果用户关闭了 JavaScript 导致很多功能失效,我并不在意。但是,如果 JavaScript 导致关键部分的页面消失了,而没有充分的理由,这就让我感到担忧。 - Chris Morgan
10
如果这是一个问题,也许可以使用这个<noscript> <font color='Red'><h1>请启用JavaScript,失败者!</h1> </font></noscript> ;-) - PandaWood
24
不需要使用任何恶意,只需使用:<noscript><style>a.main {visibility: visible;}</style></noscript> - zachleat
41
在Firefox中,载入事件需要等待字体文件加载完成,但WebKit不需要。因此这种方法不太可靠。 - Paul Irish
显示剩余3条评论

18

使用$(window).bind('load')是不合适的 - 这会等待整个页面加载完成(这可能是您想要的),而不仅仅是字体。如果您想控制@font-face的加载过程,请使用由Google和Typekit开发的WebFont Loader。

您可以将其与Google Font API、Typekit和您自己的Web字体提供商一起使用 - 尽管我作为Typekit用户从未尝试过它。

在此处阅读更多信息:http://code.google.com/apis/webfonts/docs/webfont_loader.htmlhttp://blog.typekit.com/2010/05/19/typekit-and-google/


1
jQuery 不是所有 JavaScript 问题的答案,WebFont Loader 是工作的正确(其他)工具的完美例子。 :-) - Sixten Otto
@SixtenOtto 不,这个解决方案使用字体工作得很好。 - yodalr

4

我可以使用谷歌网络字体(Crete Round Regular和Open Sans Regular with Bold)

你可以使用以下任一方式:

var fonts = $.Deferred();
WebFontConfig = { google: { families: [ 'Crete+Round::latin', 'Open+Sans:400,700:latin' ] } , active : function() { fonts.resolve(); } };
(function() {
    var wf = document.createElement('script');
    wf.src = ('https:' == document.location.protocol ? 'https' : 'http') + '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
    wf.type = 'text/javascript';
    wf.async = 'true';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(wf, s);
})();
fonts.done(function(){ alert('fonts'); });

或者这样:
WebFontConfig = { google: { families: [ 'Crete+Round::latin', 'Open+Sans:400,700:latin' ] } , active : function() { alert('fonts'); } };
(function() {
    var wf = document.createElement('script');
    wf.src = ('https:' == document.location.protocol ? 'https' : 'http') + '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
    wf.type = 'text/javascript';
    wf.async = 'true';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(wf, s);
})();

请注意,在第一个选项中,我使用了jQuery Deferred Object。

1

或许...

创建一个z-index: -10的div,并用大量文本(使用“正常”字体)填充它。在document.ready()或其他事件中:

var originalnumber = $( div ).width() + $( div ).height() + $( div ).offset().top + $( div ).offset().left;

$( div ).css( 'font-family', 'MyPrettyWebfont' );

var interval = setInterval( function() {
    var number = $( div ).width() + $( div ).height() + $( div ).offset().top + $( div ).offset().left;

    if ( number !== originalnumber ) {
        // webfont is loaded and applied!
        clearInterval( interval );
    }
}, 10 );

0

I got the same problem. And somehow i can't get Google webfont loader to work with ttf font (especially chinese fonts) or send a jquery response when font is loaded.

So I came up with this a more basic solution, useful when changing font dynamically after page is loaded. it shows when the font face is properly loaded.

First i create a dummy div and then fill it with 'abcd' and then give it a font size 40, record the width of the div. When the 'change font' event is invoked via a button or anything, it should track the dummy div width changes. Width changes would represent that the font has change.

HTML CODE

<style>
    @font-face {
     font-family: 'font1';
     src:url('somefont.ttf')  format('truetype');
     }
</style>
<div id="dummy" style="border:1px solid #000; display:inline-block">abdc</div>

<!--simple button to invoke the fontchange-->
<input type="button" onclick="javascript:changefont('font1')" value="change the font"/>

JQuery

//create a variable to track initial width of default font
var trackwidth

//when change font is invoke
function changefont(newfont)
{
   //reset dummy font style
    $('#dummy').css('font-family','initial !important;');
    $('#dummy').css({'font-size':'40px'});
    $('#dummy').html('abcd');

    //ensure that trackwidth is only recorded once of the default font
    if(trackwidth==null)
    {
       trackwidth=( $('#dummy').width());
    }

    //apply new font
    $('#dummy').css('font-family',newfont);
    checkwidth()
}

function checkwidth()
{
   //check if div width changed
   if($('#dummy').width() == trackwidth)
    {
        //if div never change, detect again every 500 milisecs
        setTimeout(checkwidth, 500);
    }
    else
    {
      //do something, font is loaded!
    }
}


-5

我遇到了同样的问题,尝试使用readyfunction()、bind()函数以及其他一些我找到的方式,但都没起作用。最后我找到了解决方法,只需要在加载动画之前加上一段延迟…像这样:

$(document).ready(function() {

setTimeout(function (){
   // The animation
},150);

}); // end ready

我知道这不是最好的解决方案,所以有人能告诉我更好的吗?

谢谢!


@Fernando:请停止发布这些糟糕的编程技巧。 - Samrat Saha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接