技术生活

动态加载字体及IE&Edge兼容的问题

在IE和Edge上验证前面写的动态加载图片及字体等静态资源代码的时候,发现总是报 InvalidStateError 的错误,查了一大圈,总算找到解决办法了。

原因:在 IE和Edge 使用XmlHttpRequest的时候,不能在 request.open()之前,设置responseType。如下边的写法,在IE和Edge上是会报错的。

...
// For the Fucking IE&Edge browser, this line MUST lay after 'request.open()'
request.responseType = 'arraybuffer';
// Downloading a font from the path
request.open('GET', fontFileUrl);
...

正确的写法是:

...
// Downloading a font from the path
request.open('GET', fontFileUrl);
// For the Fucking IE&Edge browser, this line MUST lay after 'request.open()'
request.responseType = 'arraybuffer';
...

还有另外一个问题: IE和Edge 不支持FontFace这个javascript类。作为在 IE和Edge 的替代方案,使用data url方式来加载web font到资源中。具体方式如下。

    /*
    References:
        https://usefulangle.com/post/74/javascript-dynamic-font-loading
    */
    function loadFont(fontFaceName, fontFileUrl, token) {
        var request = new XMLHttpRequest();
        request.addEventListener('readystatechange', function (e) {
            if (request.readyState == 2 && request.status == 200) {
                // Download is being started
            }
            else if (request.readyState == 3) {
                // Download is under progress
            }
            else if (request.readyState == 4) {
                // Downloading has finished

                // request.response holds the binary data of the font
                // For Chrome, Firefox, Opera, Safari etc. modern browsers
                if('fonts' in document){
                    var junction_font = new FontFace(fontFaceName, request.response);
                    junction_font.load().then(function (loaded_face) {
                        document.fonts.add(loaded_face);
                        //document.body.style.fontFamily = '"' + fontFaceName + '"';
                        createStyleTag("body{font-family:" + fontFaceName + "}");
                    }).catch(function (error) {
                        // error occurred
                    });
                } else {
                    // For the fucking IE&Edge browser, Set fonts as data url with base64 format
                    /*
                    @font-face{font-family:Lato;src:url('data:application/x-font-woff;charset=utf-8;base64,######');}
                    */
                    if('head' in document){
                        var base64FontHeader = "data:application/x-font-woff;charset=utf-8;base64,";
                        var codes = new Uint8Array(request.response);

                        var bin = "";
                        for (var i=0; i<codes.byteLength; i++) {
                            bin += String.fromCharCode(codes[i]);
                        }

                        var base64String = btoa(bin);
                        var dataUrl = base64FontHeader + base64String;
                        var cssText = "@font-face{font-family:" + fontFaceName + ";src:url('" + dataUrl + "');}";
                        createStyleTag(cssText);
                        createStyleTag("body{font-family:" + fontFaceName + "}");
                    }
                }
            }
        });

        request.addEventListener('progress', function (e) {
            var percent_complete = (e.loaded / e.total) * 100;
            //console.log(percent_complete);
        });

        // Downloading a font from the path
        request.open('GET', fontFileUrl);
        // For the Fucking IE&Edge browser, this line MUST lay after 'request.open()'
        request.responseType = 'arraybuffer';

        // set request header
        request.setRequestHeader("x-token", token);
        request.send();
    }

    function createStyleTag(cssText) {
        if (cssText) {
            var head = document.head || document.getElementsByTagName('head')[0];
            var style = document.createElement('style');
            style.appendChild(document.createTextNode(cssText));
            head.appendChild(style);
        }
    }

以下是动态加载的字体在IE11和Edge上运行的效果:

最后一个扯淡的事情,DOM对象在IE和Edge上不支持remove。也就是说,DOM对象不能在 IE和Edge 上把自己删除掉,得使用其父DOM对象调用removeChild把它删除掉,如:

createStyleTag(document.getElementById("temp_div_for_style").textContent);
//DOM can't remove itself in the fucking IE&Edge browsers.
//document.getElementById("temp_div_for_style").remove();

//You can remove DOM by it's parent DOM's removeChild method.
var child = document.getElementById("temp_div_for_style");
child.parentElement.removeChild(child);

发表评论

电子邮件地址不会被公开。 必填项已用*标注