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加载完成。

  • 相关阅读:
    [NOI2002]银河英雄传说
    Splay普及版
    线段树普及版
    长连接与短连接
    【HTTP】中Get/Post请求区别
    【HTML】知识笔记
    SVN使用教程总结
    《人生只有一次,去做自己喜欢的事》读书笔记
    【HTTP】无状态无连接的含义
    【HTML】解析原理
  • 原文地址:https://www.cnblogs.com/lydialee/p/7569479.html
Copyright © 2011-2022 走看看