zoukankan      html  css  js  c++  java
  • javascript创建css、js,onload触发callback兼容主流浏览器的实现

    http://www.fantxi.com/blog/archives/load-css-js-callback/

    由于需要写个函数,既可以加载css,又可以加载js,所以对各主流浏览器对加载js、css后是否触发onload事件做了个测试。当然,为了兼容,首先要考虑的是会用到onload和onreadystatechange,但他们并不是万能的。加载js文件onload触发callback不算大问题。css比较特殊,因为Webkeit/FF下加载css不会触发onload事件。所以研究了一晚上才找到个兼容的办法,分享如下:

    首先测试元素直接写在页面

    1. <link onload="alert('css onload')" rel="stylesheet" href="http://localhost/css/123.css"/>
    2. <script onload="alert('js onload')" src="http://localhost/123.js"></script>
    3. <link onreadystatechange="alert('css readystatechange')" rel="stylesheet" href="http://localhost/css/123.css"/>
    4. <script onreadystatechange="alert('js readystatechange')" src="http://localhost/123.js"></script>

    CSS onload: 支持: IE6-9/OP, 不支持: FF/Webkit(SF/CM)
    JS onload: 支持: IE9/OP/FF/Webkit, 不支持: IE6-8

    CSS onreadystatechange: 支持: IE6-9, 不支持: OP/FF/Webkit
    JS onreadystatechange: 支持: IE6-9, 不支持: OP/FF/Webkit

    测试js创建元素

    1. var doc = document,
    2.     head = doc.getElementsByTagName("head")[0],
    3.     css, js;
    4.  
    5. css = doc.createElement('link');
    6. css.href = 'http://localhost/123.css?2';
    7. css.rel = 'stylesheet';
    8. head.appendChild(css);
    9.  
    10. js = doc.createElement('script');
    11. js.src = 'http://localhost/123.js?2';
    12. head.appendChild(js);
    13.  
    14. css.onload = function(){
    15.     alert('css onload')
    16. }
    17. js.onload = function(){
    18.     alert('js onload')
    19. }
    20. css.onreadystatechange = function(){
    21.     alert('css readystatechange')
    22.     //alert(this.readyState) //IE可以得到loading/complete, OP为undefined
    23. }
    24. js.onreadystatechange = function(){
    25.     alert('js readystatechange')
    26.     //alert(this.readyState) //IE可以得到loading/loaded, OP为loaded
    27. }

    CSS/JS onload:(同元素直接写在页面是一样的)

    CSS onreadystatechange: 支持: IE6-9/OP, 不支持: FF/Webkit (这里有区别,OP支持js创建的css元素,但readyState为undefined)
    JS onreadystatechange: 支持: IE6-9/OP, 不支持: FF/Webkit (这里有区别,OP支持js创建的js元素,readyState为loaded)

    所以为了更大的兼容,onload、onreadystatechange都要写上,代码类似下面:

    1. // 先把js或者css加到页面: head.appendChild(node);
    2. // onload为IE6-9/OP下创建CSS的时候,或IE9/OP/FF/Webkit下创建JS的时候
    3. // onreadystatechange为IE6-9/OP下创建CSS或JS的时候
    4. node.onload = node.onreadystatechange = function(){
    5.         // !this.readyState 为不支持onreadystatechange的情况,或者OP下创建CSS的情况
    6.         // this.readyState === "loaded" 为IE/OP下创建JS的时候
    7.         // this.readyState === "complete" 为IE下创建CSS的时候
    8.         if (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") {
    9.                 node.onload = node.onreadystatechange = null; //防止IE内存泄漏
    10.                 alert('loaded, run callback');
    11.         }
    12. }

    jquery、kissy的源码,判断加载成功的核心部分差不多都这样实现的。

    存在的问题:
    1、当浏览器同时支持onload、onreadystatechange的情况会触发上面的函数两次,
    比如:
    IE9加载JS的时候,会alert两次,加载CSS的时候,alert一次,注释掉“onload、readystatechange=null”,alert两次。
    OP加载JS/CSS,alert一次,把“onload、readystatechange=null”注释也会alert两次。
    解决:
    先在外部设定个变量isLoaded = false;
    "if (!this.readyState..."上面加上个判断,如果已经加载成功就返回,比如:if (isLoaded) { return; }
    "node.onload =..."上面加上 isLoaded = ture;
    JQ有没有加这个我忘记了、KS应该是加了类似的判断了。

    2、这个方法加载JS调用callback是兼容性没问题了,但是加载CSS再callback支持情况不同了:
    IE6-9/OP可以成功alert,但是FF/Webkit不支持css的onload,解决办法:

    2.1、读取cssRules的length来判断是否加载成功,缺点不能跨域读取(非IE)。

    1. var doc = document,
    2. head = document.getElementsByTagName( 'head' )[0],
    3. link = doc.createElement('link');
    4.         //link.href = 'http://xxx.com/123.css'; //跨域
    5.         link.href = 'http://localhost/123.css';
    6.         link.rel = 'stylesheet';
    7.         head.appendChild( link );
    8.  
    9. var sheet, cssRules;
    10. if ( 'sheet' in link ) { //FF/CM/OP
    11.         sheet = 'sheet'; cssRules = 'cssRules';
    12. }
    13. else { //IE
    14.         sheet = 'styleSheet'; cssRules = 'rules';
    15. }
    16.  
    17. var _timer1 = setInterval( function() { // 通过定时器检测css是否加载成功
    18.         try {
    19.                 if ( link[sheet] && link[sheet][cssRules].length ) { // css被成功加载
    20.                         // console.log(link[sheet][cssRules]);
    21.                         clearInterval( _timer1 ); // 清除定时器
    22.                         clearTimeout( _timer2 );
    23.                         alert('loaded, run callback'); // 加载成功执行callback
    24.                 }
    25.         } catch( e ) {
    26.                 // FF看到的可能的报错:
    27.                 //本地:nsresult: "0x8053000f (NS_ERROR_DOM_INVALID_ACCESS_ERR)" ,因为没加载完成还不能读取,加载完毕就不会报错了
    28.                 //跨域:Security error, code: "1000" nsresult: "0x805303e8",因为不能跨域读取CSS。。。
    29.                 //关于跨域访问:FF/OP/CM都禁止,IE6-9都可以跨域读取css。
    30.         } finally {}
    31. }, 20 ),
    32. // 创建超时定时器,如果过10秒没检测到加载成功
    33. _timer2 = setTimeout( function() {
    34.         clearInterval( _timer1 ); // 清除定时器
    35.         clearTimeout( _timer2 );
    36.         alert('loaded ? run callback'); // 都过了这么长时间了,虽然没判断加载成功也执行callback(这里可能本身就加载失败,也可能是跨域的情况)
    37. }, 10000 );
    38. //@See: http://stackoverflow.com/questions/5537622/dynamically-loading-css-file-using-javascript-with-callback-without-jquery

    2.2、创建div#hack4loaded,并添加到页面,然后定时读取div的样式,如果样式是css里面设置的就说明加载成功,此方法不存在跨域问题,所有浏览器都OK。(css里面加上一个样式: #hack4loaded{display:none})

    1. var doc = document,
    2. head = doc.getElementsByTagName('head')[0],
    3. link = doc.createElement('link');
    4.         link.href = 'http://localhost/123.css';
    5.         link.rel = 'stylesheet';
    6.         head.appendChild( link );
    7.  
    8. var div  = document.createElement('div');
    9.     div.id = 'hack4loaded';
    10.     var getStyle = function(name){ //获取css里面设置的元素样式
    11.                 if(div.currentStyle){ // IE/OP
    12.                         return div.currentStyle[name];
    13.                 }else{ // FF/CM
    14.                         return div.ownerDocument.defaultView.getComputedStyle(div, null)[name];
    15.                 }
    16.     };
    17. document.body.appendChild(div); // 添加到页面
    18. //创建定时器,读取创建的div的样式是否是已经在css里面设置好的
    19. var _timer1= setInterval( function() {
    20.         //console.log(getStyle('display'));
    21.         if ('none' === getStyle('display')) { //样式和css里面设置的一样,说明加载成功
    22.                 clearInterval( _timer1 ); // 清除定时器
    23.                 clearTimeout( _timer2 );
    24.                 div.parentNode.removeChild(div); //移除div
    25.                 alert('loaded, run callback'); // 加载成功执行callback
    26.         }
    27. }, 50 ),
    28. // 创建超时定时器,防止css文件加载失败的情况
    29. _timer2 = setTimeout( function() {
    30.         clearInterval( _timer1 ); // 清除定时器
    31.         clearTimeout( _timer2 );
    32.         alert('fail to load'); // 这个没加载成功的可能性很大,再就是网速太慢超时了
    33. }, 10000 );

    所以总结如下:

    javascript创建js或者css,可以兼容浏览器在onload时触发callback。
    只要把上面的“node.onload = node.onreadystatechange = function()”方法,再结合2.2的创建div#hack4loaded的方法结合再一起,就可以创建一个比较完美的支持加载js、css,并可以在文件加载完毕触发callback的函数。

    由于IE/OP支持css的onload,所以写这个方法的时候可以考虑非IE/OP才用2.2的方法判断css加载完成。

  • 相关阅读:
    PAT顶级 1015 Letter-moving Game (35分)
    PAT顶级 1008 Airline Routes (35分)(有向图的强连通分量)
    PAT顶级 1025 Keep at Most 100 Characters (35分)
    PAT顶级 1027 Larry and Inversions (35分)(树状数组)
    PAT 顶级 1026 String of Colorful Beads (35分)(尺取法)
    PAT顶级 1009 Triple Inversions (35分)(树状数组)
    Codeforces 1283F DIY Garland
    Codeforces Round #438 A. Bark to Unlock
    Codeforces Round #437 E. Buy Low Sell High
    Codeforces Round #437 C. Ordering Pizza
  • 原文地址:https://www.cnblogs.com/lydialee/p/7569479.html
Copyright © 2011-2022 走看看