皮膚 顏色
板式
寬屏 窄屏

javascript異步動(dòng)態(tài)加載js和css文件的實(shí)現方法、原理

發(fā)表日期:2015-06-29    文章編輯:本站編輯   來(lái)源:未知    瀏覽次數:
主要幾個(gè)框架或者插件是如何實(shí)現異步加載事件響應的。
一.LABjs
這個(gè)項目位于github上面,其本意就是Loading And Blocking JavaScript,就是一個(gè)異步腳本加載器,相對于原始的粗暴script標簽方式而言,提供了彈性的性能優(yōu)化(做到j(luò )s文件在瀏覽器中盡可能的并行加載,并且能夠提供順序執行的保證),還能在高級瀏覽器中實(shí)現先加載然后執行的功能。作為一個(gè)異步j(luò )s加載器還是非常優(yōu)秀的。其在異步加載的成功事件響應方面的實(shí)現如下:
// creates a script load listener
function create_script_load_listener(elem,registry_item,flag,onload) {
        elem.onload = elem.onreadystatechange = function() {
                if ((elem.readyState && elem.readyState != "complete" && elem.readyState != "loaded")
               || registry_item[flag]) return;
                        elem.onload = elem.onreadystatechange = null;
                        onload();
        };
}
從上面可見(jiàn),基本上就是利用onload事件和onreadystatechange事件來(lái)完成的,最后一個(gè)registry_item[flag]就是對同源的文件可以通過(guò)AJAX來(lái)實(shí)現的。但是沒(méi)有涉及到css文件的加載,也沒(méi)提到j(luò )s文件不存在的時(shí)候如何來(lái)檢測。
二.RequireJS
RequireJS主要定位于a file and module loader for javascript,就是作為一種模塊化開(kāi)發(fā)過(guò)程中的模塊加載器,由于模塊是以文件形式存在,所以也就相當于一個(gè)文件加載器,但是實(shí)現了模塊管理的功能。其主要也仍然是在處理js文件的加載,沒(méi)有考慮css文件的加載。那我們看一下他主要的事件監聽(tīng)實(shí)現吧:
 //Set up load listener. Test attachEvent first because IE9 has
 //a subtle issue in its addEventListener and script onload firings
 //that do not match the behavior of all other browsers with
 //addEventListener support, which fire the onload event for a
 //script right after the script execution. See:
 //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution
 //UNFORTUNATELY Opera implements attachEvent but does not follow the script
 //script execution mode.
if (node.attachEvent &&
    //Check if node.attachEvent is artificially added by custom script or
    //natively supported by browser
    //read https://github.com/jrburke/requirejs/issues/187
    //if we can NOT find [native code] then it must NOT natively supported.
    //in IE8, node.attachEvent does not have toString()
     //Note the test for "[native code" with no closing brace, see:
     //https://github.com/jrburke/requirejs/issues/273
     !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0)
     && !isOpera) {
      //Probably IE. IE (at least 6-8) do not fire
      //script onload right after executing the script, so
      //we cannot tie the anonymous define call to a name.
      //However, IE reports the script as being in 'interactive'
      //readyState at the time of the define call.
          useInteractive = true;
          node.attachEvent('onreadystatechange', context.onScriptLoad);
      //It would be great to add an error handler here to catch
      //404s in IE9+. However, onreadystatechange will fire before
      //the error handler, so that does not help. If addEventListener
      //is used, then IE will fire error before load, but we cannot
      //use that pathway given the connect.microsoft.com issue
      //mentioned above about not doing the 'script execute,
      //then fire the script load event listener before execute
       //next script' that other browsers do.
       //Best hope: IE10 fixes the issues,
      //and then destroys all installs of IE 6-9.
      //node.attachEvent('onerror', context.onScriptError);
} else {
      node.addEventListener('load', context.onScriptLoad, false);
      node.addEventListener('error', context.onScriptError, false);
}
從上面的注釋可以明白對于IE6-8都是采用的onreadystatechange來(lái)監聽(tīng)加載成功事件,而對于404的事件是沒(méi)有辦法做出區分的,所以也就沒(méi)能完成此功能,只是對于高級瀏覽器比如chrome之類(lèi)的采用了onload監聽(tīng)成功事件,onerror監聽(tīng)失敗事件。由于主要針對js文件的加載,所以也沒(méi)有針對css文件的加載做出明確的實(shí)現。
三.YUI3
YUI3作為一款非常優(yōu)秀的js框架,其框架的代碼都經(jīng)過(guò)了yahoo的使用場(chǎng)景考驗,應該是非常完善和趨于完美的實(shí)現了,那我們同樣來(lái)窺探一下他實(shí)現異步獲取文件的模塊代碼吧:
// Inject the node.
if (isScript && ua.ie && (ua.ie < 9 || (document.documentMode && document.documentMode < 9))) {
        // Script on IE < 9, and IE 9+ when in IE 8 or older modes, including quirks mode.
        node.onreadystatechange = function () {
                if (/loaded|complete/.test(node.readyState)) {
                        node.onreadystatechange = null;
                        onLoad();
                }
        };
} else if (!isScript && !env.cssLoad) {
        // CSS on Firefox <9 or WebKit.
        this._poll(req);
} else {
        // Script or CSS on everything else. Using DOM 0 events because that
        // evens the playing field with older IEs.

        if (ua.ie >= 10) {

                // We currently need to introduce a timeout for IE10, since it
                // calls onerror/onload synchronously for 304s - messing up existing
                // program flow.

                // Remove this block if the following bug gets fixed by GA
                // https://connect.microsoft.com/IE/feedback/details/763871/dynamically-loaded-scripts-with-304s-responses-interrupt-the-currently-executing-js-thread-onload
                node.onerror = function() { setTimeout(onError, 0); };
                node.onload = function() { setTimeout(onLoad, 0); };
        } else {
                node.onerror = onError;
                node.onload = onLoad;
        }

        // If this browser doesn't fire an event when CSS fails to load,
        // fail after a timeout to avoid blocking the transaction queue.
        if (!env.cssFail && !isScript) {
                cssTimeout = setTimeout(onError, req.timeout || 3000);
        }
}

this.nodes.push(node);
insertBefore.parentNode.insertBefore(node, insertBefore);
從YUI3的實(shí)現來(lái)看完成了js和css文件的加載,基本上符合我們的要求。對于js文件,在IE6-8用onreadystatechange事件來(lái)監聽(tīng),但沒(méi)有辦法監聽(tīng)到error事件所以放棄了;其他瀏覽器則通過(guò)onload和onerror來(lái)實(shí)現,基本上和上面LABjs和Requirejs類(lèi)似。對于css文件,在IE6-8上面同樣采用的是onreadystatechange來(lái)實(shí)現,并且同樣沒(méi)辦法來(lái)實(shí)現error事件的監聽(tīng);其他瀏覽器如果支持onload事件則采用此方法,如果不支持(比如firefox<7和一些低版本的webkit內核)則只能通過(guò)不斷的輪詢(xún)css節點(diǎn)來(lái)實(shí)現了。從注釋當中可以看出,在IE10下面服務(wù)器緩存設置返回304的時(shí)候有一個(gè)bug,需要通過(guò)異步的方式來(lái)觸發(fā)監聽(tīng)方法,具體可以再測試一下。YUI3中對于css加載的輪詢(xún)方式如下:
if (isWebKit) {
        // Look for a stylesheet matching the pending URL.
        sheets = req.doc.styleSheets;
        j = sheets.length;
        nodeHref = req.node.href;

        while (--j >= 0) {
                if (sheets[j].href === nodeHref) {
                        pendingCSS.splice(i, 1);
                        i -= 1;
                        self._progress(null, req);
                        break;
                }
        }
} else {
        // Many thanks to Zach Leatherman for calling my attention to
        // the @import-based cross-domain technique used here, and to
        // Oleg Slobodskoi for an earlier same-domain implementation.
        //
        // See Zach's blog for more details:
        // http://www.zachleat.com/web/2010/07/29/load-css-dynamically/
        try {
                // We don't really need to store this value since we never
                // use it again, but if we don't store it, Closure Compiler
                // assumes the code is useless and removes it.
                hasRules = !!req.node.sheet.cssRules;

                // If we get here, the stylesheet has loaded.
                pendingCSS.splice(i, 1);
                i -= 1;
                self._progress(null, req);
        } catch (ex) {
                // An exception means the stylesheet is still loading.
        }
}
從上面輪詢(xún)的方式來(lái)看,對于webkit內核的則通過(guò)檢查style的sheet節點(diǎn)是否附加上了來(lái)測試,其他比如firefox和opera則通過(guò)檢查sheet.cssRules是否生效來(lái)完成。但是始終都是沒(méi)有辦法解決404的問(wèn)題。所以也就只能這樣了。。。
四.jQuery
大名鼎鼎的jQuery在實(shí)現ajax封裝了所有的異步加載功能的時(shí)候,為script加載專(zhuān)門(mén)分了文件的,具體可以看到如下實(shí)現:
script = jQuery("<script>").prop({
        async: true,
        charset: s.scriptCharset,
        src: s.url
}).on(
        "load error",
        callback = function( evt ) {
                script.remove();
                callback = null;
                if ( evt ) {
                        complete( evt.type === "error" ? 404 : 200, evt.type );
                }
        }
);
document.head.appendChild( script[ 0 ] );
從上面代碼看基本上和上面類(lèi)似,看來(lái)對于js而言沒(méi)有什么太多的方法,所以基本上按照以上幾種實(shí)現即可。jquery并沒(méi)有對css文件加載做專(zhuān)門(mén)的處理,所以還無(wú)從參考。
五.Seajs
Seajs在阿里系還是有很大的使用范圍的,并且目前推廣的還不錯,所以陸續有很多公司開(kāi)始采用了。其也主要是推行模塊化開(kāi)發(fā)的方式,因此也會(huì )涉及到異步記載模塊文件的方式,所以也涉及到了文件的異步加載。其request模塊實(shí)現如下:
function addOnload(node, callback, isCSS) {
  var missingOnload = isCSS && (isOldWebKit || !("onload" in node))

  // for Old WebKit and Old Firefox
  if (missingOnload) {
    setTimeout(function() {
      pollCss(node, callback)
    }, 1) // Begin after node insertion
    return
  }

  node.onload = node.onerror = node.onreadystatechange = function() {
    if (READY_STATE_RE.test(node.readyState)) {

      // Ensure only run once and handle memory leak in IE
      node.onload = node.onerror = node.onreadystatechange = null

      // Remove the script to reduce memory leak
      if (!isCSS && !configData.debug) {
        head.removeChild(node)
      }

      // Dereference the node
      node = undefined

      callback()
    }
  }
}

function pollCss(node, callback) {
  var sheet = node.sheet
  var isLoaded

  // for WebKit < 536
  if (isOldWebKit) {
    if (sheet) {
      isLoaded = true
    }
  }
  // for Firefox < 9.0
  else if (sheet) {
    try {
      if (sheet.cssRules) {
        isLoaded = true
      }
    } catch (ex) {
      // The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR"
      // to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0
      // in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"
      if (ex.name === "NS_ERROR_DOM_SECURITY_ERR") {
        isLoaded = true
      }
    }
  }

  setTimeout(function() {
    if (isLoaded) {
      // Place callback here to give time for style rendering
      callback()
    }
    else {
      pollCss(node, callback)
    }
  }, 20)
}
從seajs的實(shí)現來(lái)看,主要完成了js和css的異步加載,其主要實(shí)現還是和YUI3的get模塊實(shí)現方式基本一致。并且實(shí)現方式還是簡(jiǎn)單粗暴的,具體細節還不如YUI3的實(shí)現精細,但是對于大多數場(chǎng)景還是夠用了的。
另外從labjs和seajs上面可以注意一個(gè)細節,為了防止內存溢出,還是在js文件加載完畢之后會(huì )刪除其對應的script節點(diǎn)。因為對于js而言已經(jīng)執行,其內存中已經(jīng)保存了相關(guān)的環(huán)境變量,css文件則不一樣刪除則會(huì )將對應的style樣式一并清除。
—————分割線(xiàn)———
上面討論了幾種實(shí)現方式,看來(lái)js都比較好處理,大家也都實(shí)現的很簡(jiǎn)單,主要分IE6-8采用onreadystatechange事件,判斷readystate狀態(tài)來(lái)完成;其他瀏覽器則通過(guò)監聽(tīng)onload事件來(lái)完成,但都無(wú)法完全通過(guò)onerror事件來(lái)監聽(tīng)404狀態(tài)。對于css文件則實(shí)現比較難一點(diǎn),如果瀏覽器本身支持onload方法便好說(shuō),不支持則通過(guò)輪詢(xún)sheet的cssRules是否生效或者對應的節點(diǎn)是否生成。難道就沒(méi)有好一點(diǎn)的辦法么?
通過(guò)google查詢(xún),可以通過(guò)new Image()的方式來(lái)加載css文件的地址,然后由于mime類(lèi)型錯誤,所以會(huì )觸發(fā)img的onerror事件從而來(lái)達到模擬css文件加載成功的事件,如果不支持此方法的再沒(méi)辦法的采用輪詢(xún)的方式來(lái)完成。那到底什么時(shí)候采用此方式呢?為此,我做了一個(gè)測試頁(yè)面,來(lái)測試各種瀏覽器的對于js和css文件的異步加載事件的支持情況,具體代碼如下:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<div id="testresult" style="margin:50px;">-------------------------start----------------------<br/></div>

        <script>
/**
 *@fileoverview the loader plugin for asynchronous loading resources
 *@author ginano
 *@website
 *@date 20130228
 */
(function(){
   var headEl=document.getElementsByTagName("head")[0],
       dom=document.getElementById('testresult');

   var Loader={
       /**
       *加載js文件
       * @param {Object} url
       */
      importJS:function(url,str){
            var head ,
                script;

            head = headEl;
            script = document.createElement("script");
            script.type = "text/javascript";

           script.onreadystatechange=function(){
                 dom.innerHTML+='suppport:js-readystatechange-'+this.readyState+' event-----------------------'+str+'<br/>';
            };

           script.onload=function(){
                dom.innerHTML+='suppport:js-load event-----------------------'+str+'<br/>';
            };

            script.onerror=function(){
                     dom.innerHTML+='suppport:js-error event-----------------------'+str+'<br/>';
            };

            script.src = url;
            head.appendChild(script);
      },
      /**
       *加載css文件
       * @param {Object} url
       */
      importCSS:function(url,str){
            var head,
                link,
                img,
                ua;

            head = headEl;
            link = document.createElement("link");
            link.rel="stylesheet";
            link.type = "text/css";
            link.href=url;

                   link.onerror=function(){
               dom.innerHTML+='<div style="color:green">suppport:css-error event-----------------------'+str+'<br/></div>';
           };
           link.onload=function(){
                dom.innerHTML+='<div style="color:green">suppport:css-load event-----------------------'+str+'<br/></div>';
            };
                    link.onreadystatechange=function(){
                 dom.innerHTML+='<div style="color:green">suppport:css-readystatechange-'+this.readyState+' event-----------------------'+str+'<br/></div>';
           };
            head.appendChild(link);
                    img=document.createElement('img');
            img.onerror=function(){
                dom.innerHTML+='<div style="color:green">suppport:css-img-error event-----------------------'+str+'<br/></div>';
            };
            img.src=url;

      }
   };
   dom.innerHTML+='browser Info:'+window.navigator.userAgent+'<br/>';
   //測試正常文件
   Loader.importJS(
       'http://yui.yahooapis.com/2.9.0/build/yahoo/yahoo-min.js',
        'rightJS'
   );
   //測試404js文件
    Loader.importJS(
       'http://www.ginano.net/1.js',
        'wrongJS'
   );
  //測試css文件
   Loader.importCSS(
       'http://yui.yahooapis.com/2.9.0/build/fonts/fonts.css',
        'rightCSS'
   );
//測試404css文件
   Loader.importCSS(
       'http://www.ginano.net/1.css',
        'wrongCSS'
   );

})();
</script>
</body>
</html>
由于需要各種瀏覽器測試結果,所以在http://browsershots.org/上面打開(kāi)測試頁(yè)面http://www.ginano.net/test-browser-load-js-css-event.html ,跑了半天每個(gè)瀏覽器都有一張如下所示的截屏。

 
該平臺支持173種瀏覽器,通過(guò)將結果整理得到如下的數據:



事件支持情況 
瀏覽器
onreadystatechange
onload
onerror
Img.onerror



5~8
JS
200
Loading,loaded,第二次為complete





404
Loading,loaded,第二次為complete





CSS
200
Loading,complete
ok

ok


404
Loading,complete
ok

ok


9.0-10
JS
200
Loading,loaded
ok




404
Loading,loaded

ok



CSS
200
Loading,complete
ok

ok


404
Loading,complete
ok

ok


Chrome
1~9 
(webkit:530-534)
JS
200

ok




404


ok



CSS
200






404






10~19 
(webkit:534.15-535.21)
JS
200

ok




404


ok



CSS
200



ok


404



ok


20~26 
(webkit:536.11-537.11)
JS
200

Ok




404


ok



CSS
200

ok

ok


404


ok
ok


Firefox
1~8
JS
200

ok




404


ok



CSS
200



ok


404



ok


9~20
JS
200

Ok




404


Ok



CSS
200

Ok

ok


404


ok
ok


Opera
<=11.61
JS
200
Loaded
Ok




404






CSS
200
Undefined?
Ok

ok


404



ok


404



ok


備注: 
在9.64版本,js兩種情況都還會(huì )觸發(fā)onreadystatechange-interactive;
在11.61版本中,js-404會(huì )觸發(fā)onerror事件。
 


11.64~12.50
JS
200

OK




404


OK



CSS
200

OK

OK


404



OK


Safari
3.2.3 
(webkit:525.28)
JS
200

ok




404

ok




CSS
200






404






5.0 
(webkit:533-534)
JS
200

ok




404


ok



CSS
200



ok


404



ok


6.0 
(webkit:536)
JS
200

ok




404


ok



CSS
200

ok

ok


404


ok
OK



從上面的結果總結規律如下:
1.js文件
1.1 IE8及以下版本,通過(guò)onreadystatechange事件監聽(tīng),判斷readystate狀態(tài)是否為load或者complete從而觸發(fā)成功事件。具體可查閱上表
1.2 其他瀏覽器直接通過(guò)onload事件即可完成加載成功事件的監聽(tīng)。
1.3 由于始終無(wú)法保證onerror事件的支持,只是對能夠支持的加上即可
2.css文件
2.1 所有瀏覽器對onerror的支持都不完美,所以只是盡量處理
2.2 IE瀏覽器/firefox9.0級以上/opera/chrome瀏覽器20及以上/safari瀏覽器6.0以上都支持css的onload事件,因此通過(guò)監聽(tīng)onload即可。
2.3 chrome瀏覽器9.10到19.0/safari瀏覽器5.0到5.9/firefox瀏覽器8.9一下則通過(guò)img的onerror事件即可模擬出css文件的加載成功事件
2.4 其他瀏覽器,比如chrome瀏覽器9.0及以下則只能通過(guò)輪詢(xún)css樣式節點(diǎn)是否附加成功來(lái)判斷了
備注:YUI3的注釋當中提到了IE10的bug不知道修復與否,但是目前測試結果是ok的所以沒(méi)有做單獨處理。
鑒于上面的基本規律,我再問(wèn)的文件加載器模塊當中的實(shí)現代碼如下:
/**
 *@fileoverview the loader plugin for asynchronous loading resources
 * there isn't method to resolve the problem of 404
 *@author ginano
 *@website
 *@date 20130228
 */
define('modules/loader',[
        'modules/class',
        'modules/ua',
        'modules/util'
    ],function(Class,UA,Util){
   var LoadedList={},
       headEl=document.getElementsByTagName("head")[0],
       isFunction=function(f){
            return f instanceof Function;
       };

   var Loader=new Class('modules/loader',{
       /**
       *加載js文件
       * @param {Object} url
       */
      static__importJS:function(url,callback){
            var head ,
                script,
                //成功之后做的事情
                wellDone=function(){
                    LoadedList[url]=true;
                    clear();
                    Util.log('load js file success:'+url);
                    callback();
                },
                clear=function(){
                   script.onload=script.onreadystatechange=script.onerror=null;
                   head.removeChild(script);
                   head=script=null;
                };

            if(LoadedList[url]){
                isFunction(callback)&&callback();
                return;
            }
            head = headEl;
            script = document.createElement("script");
            script.type = "text/javascript";

           script.onerror=function(){
               clear();
               Util.log('load js file error:'+url);
           };

            if(isFunction(callback)){
                //如果是IE6-IE8
                if(UA.browser=='ie' && UA.version<9){
                    script.onreadystatechange=function(){
                        //當第一次訪(fǎng)問(wèn)的時(shí)候是loaded,第二次緩存訪(fǎng)問(wèn)是complete
                        if(/loaded|complete/.test(script.readyState)){
                            wellDone();
                        }
                    }
                }else{
                    script.onload=function(){
                       wellDone();
                    }
                }
                //始終保證callback必須執行,所以需要定時(shí)器去完成,測試結果表明早期的大量的瀏覽器還不支持
                //timer=setTimeout(function(){
                // wellDone();
                //},10000);
            }

            script.src = url;
            head.appendChild(script);
      },
      /**
       *加載css文件
       * @param {Object} url
       */
      static__importCSS:function(url,callback){
            var head,
                link,
                img,
                firefox,
                opera,
                chrome,
                poll,
                //成功之后做的事情
                wellDone=function(){
                    LoadedList[url]=true;
                    clear();
                    Util.log('load css file success:'+url);
                    callback();
                },
                clear=function(){
                    timer=null;
                    link.onload=link.onerror=null;
                    head=null;
                };
            if(LoadedList[url]){
                isFunction(callback)&&callback();
                return;
            }
            head = headEl;
            link = document.createElement("link");
            link.rel="stylesheet";
            link.type = "text/css";
            link.href=url;

            link.onerror=function(){
               clear();
               Util.log('load css file error:'+url);
            };
            if(isFunction(callback)){
                //如果是IE系列,直接load事件
                if(UA.browser=='ie'
                    || (UA.browser=='firefox' && UA.version>8.9)
                    || UA.browser=='opera'
                    || (UA.browser=='chrome' && UA.version>19)
                    || (UA.browser=='safari' && UA.version>5.9)

                ){

                   //IE和opera瀏覽器用img實(shí)現
                    link.onload=function(){
                        wellDone();
                    };
                    head.appendChild(link);

                }else if(
                   (UA.browser=='chrome' && UA.version>9)
                   || (UA.browser=='safari' && UA.version>4.9)
                   || UA.browser=='firefox'
                ){

                    head.appendChild(link);
                    //如果是非IE系列
                    img=document.createElement('img');
                    img.onerror=function(){
                        img.onerror=null;
                        img=null;
                        wellDone();
                    };
                    img.src=url;

                }else{//輪詢(xún)實(shí)現
                    head.appendChild(link);
                    poll=function(){
                        if(link.sheet && link.sheet.cssRules){
                            wellDone();
                        }else{
                            setTimeout(poll,300);
                        }
                    };
                    poll();
                }
            }else{
                head.appendChild(link);
            }
      },
      /**
       *異步加載所需的文件
       * @param {Array} urls
       * @param {Function} callback
       * @param {Boolean} [option=true] isOrdered 是否需要按序加載,默認是需要按序加載
       */
      static__asyncLoad:function(urls,callback,isOrdered){
          var _self=this,
              isOrder=!(isOrdered===false),
              isAllDone=false,
              now,
              i,
              urls= ('string'===typeof urls)?[urls]:urls;
              len=(urls instanceof Array) && urls.length,
              /**
               *根據后綴判斷是js還是css文件
               * @param {Object} url
               * @param {Object} done
               */
              load=function(url, done){
                  if(/\.js(?:\?\S+|#\S+)?$/.test(url)){
                      _self.importJS(url,done);
                  }else{
                      _self.importCSS(url,done);
                  }
              },
              orderLoad=function(){
                  now=urls.shift();
                  if(now){
                     load(now,orderLoad);
                  }else{
                     callback && callback();
                  }
              };
          if(!len || len<1){
              return;
          }
          //如果有順序
          if(isOrder){
              orderLoad();
          }else{
             //如果沒(méi)有順序加載
             for(i=0,now=0;i<len;i++){
                 load(urls[i],function(){
                     now+=1;
                     if(now==len){
                        callback && callback();
                     }
                 });
             }
          }
      }
   });
   return Loader;
});
經(jīng)過(guò)測試以上實(shí)現方式還是具有非常好的兼容性的,如果大家測試有什么bug可以盡管在評論中予以指正。

相關(guān)文章推薦

  
Socket 和 WebSocket 有哪些區別和聯(lián)系? WebSocket 和 HTML5 是什么關(guān)系? 必須在瀏覽器中才能使用...
  
其實(shí)對于我們一般理解的計算機內存,它算是CPU與計算機打交道最頻繁的區域,所有數據都是...
  
簡(jiǎn)介 響應式Web設計 是一種創(chuàng )建Web應用程序的新方法。一旦采用 響應式Web設計 創(chuàng )建出應用程序...
  
用div做成表格的形式,把標簽中間的空格都去掉就可以了...
  
看下面的代碼,其中連接池采用的c3p0,配置文件省略 import java.sql.Connection; import org.springframe...
  
主要幾個(gè)框架或者插件是如何實(shí)現異步加載事件響應的。 一.LABjs 這個(gè)項目位于github上面,其本...
  
html5shiv讓IE6-IE8支持HTML5標簽 越來(lái)越多的站點(diǎn)開(kāi)始使用 HTML5 標簽。但是目前的情況是還有很多人...
  
緩存 是實(shí)際工作中非常常用的一種提高性能的方法, 我們會(huì )在許多場(chǎng)景下來(lái)使用緩存。 本文通...
  
為了防止惡意用戶(hù)發(fā)布惡意內容,我們的安全分析瀏覽器都在虛擬機上運行。這使我們能夠確...
?