前段时间研究了下JS动态加载和执行顺序依赖的东东,把LABJS的源码从头扒了下:LABJS浅析。对于JS加载执行以及下载监控这,项目组在这块做的东西不少,但对于CSS加载这块的质量监控,力度就小得多了。原因很简单:JS下载失败或出错,这个页面基本就废了。CSS下载失败,大部分情况下页面还是可用的,虽然会比较臭。
但对于OPA来说,情况可能就完全不同了,CSS文件加载失败的影响相对就比较大了。
本着生命不息折腾不已的精神,又倒腾了下CSS加载这块的内容,成果如下,鉴于今天晚上11点才下班回家现在已经很困,就直接上代码了,详细分析后面补上~
删掉注释空行其实代码很少,关于如何测试、API调用都在开头声明了,demo可下载 附件 :)
1 /** 2 * CSS文件加载器,主要功能:动态加载CSS文件,支持加载完成时候的回调(成功 and 失败 情况下) 3 * 源码实现借鉴:https://github.com/rgrove/lazyload/commit/6caf58525532ee8046c78a1b026f066bad46d32d 4 * 更多关于CSS加载的坑的讨论,见:http://www.phpied.com/when-is-a-stylesheet-really-loaded/ 5 * 6 * 测试方法:1)将文件解压到服务器上(或用fiddler等本地文件替换) 2)访问demo.html即可 7 * 8 * @example 9 * loadCSS.load('style.css'); 10 * loadCSS.load('style.css', function(){ alert('style.css loaded'); }); 11 * loadCSS.load('style.css', function(obj){ alert('age is '+obj.age); }, {age: 24}); 12 * loadCSS.load(['a.css', 'b.css'], function(){ alert('a.css and b.css are all loaded'); }); 13 * 14 * 更多说明:目前只能判断CSS文件加载事件是否完成,至于是否出现404、5XX等,还判断不了 15 * 曲线救国:回调里判断CSS里定义的某个样式是否存在/生效,借此判断CSS是否下载成功,如下 16 * loadCSS.load('sytle.css', function(){ 17 * var div = document.createElement('div'); 18 * div.className = 'pre_defined_class'; //pre_defined_class 为测试用的预定义类,假设为 .pre_defined_class{display:none;} 19 * var value = getStyle(div, 'display'); 20 * if(value=='none'){ 21 * //成功 22 * }else{ 23 * //失败 24 * } 25 * }) 26 * 27 * @version 1.0 28 * @TODO: 1)静态加载的CSS文件的检测(是否成功加载)2)加载配置项 29 * @author casper chyingp@gmail.com 30 * http://www.cnblogs.com/chyingp 31 * http://www.zcool.com.cn/u/346408 32 * 33 */ 34 var LoadCSS = (function () { 35 36 //配置项,未实现 37 var CFG = { 38 POLL_INTERVAL: 50, 39 MAX_TIME: 10 40 }; 41 42 var head = document.head || document.getElementsByTagName('head')[0]; 43 var styleSheets = document.styleSheets 44 var env = getEnv(); //获取用户代理信息,为浏览器差异化加载提供判断依据 45 var queue = []; //CSS加载队列 46 /* 47 @格式1 queue队列内元素格式 48 { 49 urls: ['a.css', 'b.css', 'd.css'], 50 callback: function(param){}, //urls里面所有CSS文件加载完成后的回调方法,可选 51 obj: {age:24} //callback回调方法传入的实参 52 } 53 */ 54 55 56 function indexOf(arr, ele){ 57 var ret = -1; 58 for(var i=0,len=arr.length; i<len; i++){ 59 if(arr[i]==ele) ret = i; 60 } 61 return ret; 62 } 63 64 /** 65 * @private 66 * @description 返回用户浏览器代理信息,为判断不同浏览器提供依据 67 * @return {Object} 格式见内部代码 68 */ 69 function getEnv() { 70 var ua = navigator.userAgent; 71 var env = {}; 72 73 (env.webkit = /AppleWebKit\//.test(ua)) 74 || (env.ie = /MSIE/.test(ua)) 75 || (env.opera = /Opera/.test(ua)) 76 || (env.gecko = /Gecko\//.test(ua)) 77 || (env.unknown = true); 78 79 return env; 80 } 81 82 /** 83 * @private 84 * @description gecko内核的浏览器轮询检测方法 85 * 参考:http://www.zachleat.com/web/2010/07/29/load-css-dynamically/ 86 * @param {HTMLElement} node style节点,node.nodeName == 'STYLE' 87 * @param {Object} queueObj 见@格式1 88 */ 89 function pollGecko(node, queueObj) { 90 try { 91 92 node.sheet.cssRules; 93 94 } catch (ex) { 95 96 node.pollCount++; 97 98 if (node.pollCount < 200) { 99 100 setTimeout(function () { 101 pollGecko(node, queueObj); 102 }, 50);103 104 } else {105 106 finishLoading(node.href, queueObj); //用不用略做些延迟,防止神一样的渲染问题??107 108 }109 110 return;111 }112 113 finishLoading(node.href, queueObj);114 }115 116 117 /**118 * @private119 * @description webkit内核的浏览器轮询检测方法120 * @param {HTMLElement} node link节点,node.nodeName == 'LINK'121 * @param {Object} queueObj 见@格式1122 */123 function pollWebKit(node, queueObj) {124 125 for(var i=styleSheets.length; i>0; i--){126 127 if(styleSheets[i-1].href===node.href){128 finishLoading(node.href, queueObj);129 return;130 }131 }132 133 node.pollCount++; //轮询次数加1134 135 if (node.pollCount < 200) {136 setTimeout(function(){137 pollWebKit(node, queueObj);138 }, 50);139 } else {140 finishLoading(node.href, queueObj);141 }142 }143 144 function checkSucc(className, attr, value){145 var div = document.createElement('div');146 div.style.cssText += 'height:0; line-height:0; visibility:hidden;';147 div.className = className;148 document.body.appendChild(div);149 150 return getComputedStyle(div, attr)==value;151 }152 153 /**154 * @description 获取节点样式值——只能获取比较简单的样式的值,一些兼容性问题不是重点,在这里不做处理,有兴趣可以看下jquery源码155 * @param {HTMLElement} node dom节点156 * @param {String} attr 样式名字,如display、visibility等157 */158 function getComputedStyle(node, attr){159 var getComputedStyle = window.getComputedStyle;160 if(getComputedStyle){161 return getComputedStyle(node, null)[attr];162 }else if(node.currentStyle){163 return node.currentStyle[attr];164 }else{165 return node.style[attr];166 }167 }168 169 /**170 * @private171 * @description url对应的CSS文件加载完成时的回调(404也包括在内)172 * @param {String} url CSS文件的url173 * @param {Object} queueObj 见@格式1174 */175 function finishLoading(url, queueObj){176 var index = indexOf(queueObj.urls, url);177 queueObj.urls.splice(index, 1);178 179 if(!queueObj.urls.length){180 queueObj.callback(queueObj.obj);181 182 index = indexOf(queue, queueObj);183 queue.splice(index, 1);184 }185 }186 187 /**188 * @description 加载CSS的方法189 * @param {Array} urls 加载的CSS文件名队列190 * @param {Function} [callback] CSS文件队列全部加载完的回调191 * @param {Object} obj callback的参数192 * @param {Object} context193 * @return {Undefined}194 */195 function loadCSS(urls, callback, obj) {196 var queueObj = {197 urls: urls,198 callback: callback,199 obj: obj200 }201 queue.push(queueObj);202 203 var pendingUrls = queueObj.urls;204 for (var i = 0, len = pendingUrls.length; i < len; ++i) {205 206 var url = pendingUrls[i];207 var node ;208 if(env.gecko){209 node = document.createElement('style');210 }else{211 node = document.createElement('link');212 node.rel = 'stylesheet';213 node.href = url;214 }215 //node.setAttribute('charset', 'utf-8'); //设不设置有木有影响,持保留态度216 217 if (env.gecko || env.webkit) { //老版本webkit、gecko不支持onload218 219 node.pollCount = 0;220 queueObj.urls[i] = node.href; //轮询判断的时候用到,因为不同浏览器里面取到的node.href值会不一样,有的只有文件名,有的是完整文件名?(相对路径、绝对路径) 221 222 if (env.webkit) { //之所以要用轮询,后面讨论,@TODO: 新版本的webkit已经支持onload、onerror,优化下?223 224 pollWebKit(node, queueObj);225 226 } else {227 228 node.innerHTML = '@import "' + url + '";'; //为什么这样做,猛点击这里:http://www.phpied.com/when-is-a-stylesheet-really-loaded/229 pollGecko(node, queueObj);230 }231 232 } else {233 234 node.onload = node.onerror = function(){235 finishLoading(this.href, queueObj);236 };237 }238 239 head.appendChild(node);240 }241 }242 243 //---------------------- 对外接口!---------------------------244 return {245 246 /**247 * @description 加载CSS文件248 * 考虑:成功回调,错误回调分开?249 * @param {Array|String} urls 要加载的CSS文件的文件名(相对路径,或绝对路径),比如:'style.css', ['style.css', 'test.css']250 * @param {Function} [callback] 可选:文件加载完成后的回调(成功;或失败,如404、500等)251 * @param {Object} [obj] 可选:回调执行时传入的参数252 */253 load: function (urls, callback, obj) {254 loadCSS([].concat(urls), callback || function(){}, obj || {});255 }256 257 };258 })();
原文:https://www.cnblogs.com/chyingp/archive/2013/03/01/load_css.html
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦