zoukankan      html  css  js  c++  java
  • js 外部文件加载处理

    概述

    前端在日常工作中很大一部分时间是在思考页面的优化方案,让页面载入得更快。鉴于javascript是单线程的事件驱动语言,优化工作之一就是:控制图片、swf、iframe等大流量文件以及js和css等文件的加载顺序,让它们井然有序的进入到页面中,页面就能尽可能完整的呈现在他们眼前。而为了更好的用户体验,我们要知道每个文件触发onload事件的方案,因为它们在各个浏览器中的表现不尽相同。

    iframe的 load 事件

    在所有为IFRAME动态添加onload监听事件的方法中,只有 使用事件监听方式为 IFRAME 的 onload 事件绑定处理函数,IE6、7、8才有效。所以为 IFRAME 添加load事件完美方案如下:

     1 // 事件监听兼容方案
     2 function addEvent(elem,event,fn){
     3     if (elem.attachEvent) {
     4         elem.attachEvent('on'+event,fn)
     5     } else {
     6         elem.addEventListener(event,fn,false)
     7     }
     8 }   
     9 
    10 window.onload = function(){
    11     var iframeA = document.createElement('iframe');
    12     iframeA.src = 'http://www.baidu.com'
    13     addEvent(iframeA,'load',function(){
    14         document.body.bgColor = '#000'; // 回调函数
    15     });
    16     document.body.appendChild(iframeA);
    17 }

    优化页面建议不要嵌套iframe,但是在内部项目还是很常见。其实在IE中,监控iframe加载完毕还可以采取监听 onreadystatechange 事件。

    flash 的 load 事件

    解决flash的 load主要是两个问题:获取flash对象和flash何时加载完毕。

    首先第一个问题:如果object和embed用同样的ID,获取flash对象的时候,IE会认不出。解决方案:

    • js判断IE和非IE,IE中是object,非IE中是embed。
    • 通过flash对象的PercentLoaded方法,检测其值是否为100。

    html代码

    <div id="load">flash加载中....</div>
    <div id="swfWrap"></div>

    css代码

    #swfWrap{200px;height:200px;}
    #load{200px;color:#fff;text-align:center;background-color:#eee;}

    js代码

     1 // 封装通过ID获取
     2 function $(id){
     3     return document.getElementById(id)
     4 }
     5 
     6 var isIE = navigator.appVersion.indexOf("MSIE") != -1 ? true: false;
     7 
     8 
     9 // 监听flash是否加载成功
    10 function listenMovie(flash){
    11     try{
    12         return Math.floor(flash.PercentLoaded()) == 100 ;
    13     }catch(e){
    14         return false;
    15     }
    16 }
    17 
    18 // 获取FLASH对象
    19 function thisMovie(movieName) {
    20     if (isIE) {
    21         return window[movieName];
    22     }
    23     else {
    24         return document[movieName];
    25     }
    26 }
    27 
    28 // 创建flash
    29 function createFlash(id,url){
    30     var html = '<object id="flash" height="200" width="200" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">'+
    31                '<PARAM NAME="FlashVars" VALUE="">'+ 
    32                 '<PARAM NAME="Movie" VALUE="'+url+'">'+ 
    33                 '<PARAM NAME="WMode" VALUE="Transparent">'+ 
    34                 '<PARAM NAME="Quality" VALUE="High">'+  
    35                 '<PARAM NAME="AllowScriptAccess" VALUE="always">'+  
    36                 '<embed type="application/x-shockwave-flash" src="'+url+'" id="flashFF" name="flashFF" wmode="window" quality="high" width="200" height="200"></embed>'+
    37                 '</object>';
    38     $(id).innerHTML = html;
    39 }
    40 
    41 window.onload = function(){
    42     createFlash('swfWrap','flips2.swf')
    43     var flashObj = isIE ? thisMovie("flash") : thisMovie("flashFF");
    44     var intervalID =  setInterval(function(){
    45         if (listenMovie(flashObj)) {
    46             clearInterval(intervalID);
    47             intervalID = null;
    48             $('load').innerHTML = 'flash加载完毕';
    49         }
    50     },60)
    51 }

    其中object中的 classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" 不能去掉,不然IE6下会获取取不到flash对象;embed的name值也不能去掉,不然chrome也获取不到。

    这里再提下flash的通信的问题,可以参考 这里,解决方法就是将EMBED的 swliveconnect 属性设置成true,然后就可以跟flash通信了。

    IMG的 load 事件

    img的load事件,我们使用 new Image()。这里我们得注意 complete 事件。研究网上的得出以下代码:

     1 var img = new Image(); 
     2 img.src= "http://i1.hoopchina.com.cn/user/627/17191627/17191627_big_3.jpg";
     3 if (img.complete || img.width) {
     4     alert("该图片已经在缓存中,不需要再下载")
     5     alert(img.height)
     6 } else {
     7     img.onload = function() {
     8         alert("图片加载完成");
     9         alert(img.height)   
    10     }
    11 }

    这里网上很多说法是这样子说的,只要加载过一次图片,img.complete就变成true了,图片就存进浏览器缓存,下次再加载就直接忽略了onload事件,直接从缓存里去读取,而不是再重新去下载。但是我在多个变化条件下(同一个浏览器、同一个标签页、清楚缓存、一个页面存在多个相同图片)测试发现:

    在f5刷新后,除了火狐是直接从缓存中读取的,也就是执行 if(img.complete || img.width)语句下的,其他的浏览器都是执行else语句里的代码,重新下载图片;ctrl+f5的话,则所有浏览器都是重新下载图片的。

    那么 img.complete 的真正意义在于什么呢?一张页面中,如果存在多个图片地址相同的 img 标签 ,浏览器只会请求一次图片链接,而不是每个img都去请求。

    使用 new Image() 请求相同的 gif 图片时,img.complete 貌似不准确,不知道什么原因,难道是因为 gif 动态图是由多张静态图组成?。

    而网上说的,将src赋值放在onload事件之后,并不是从根本原因上解决问题。

    JS的 load 事件

    首先准备一下即将要用到的辅助函数:

     1 function delay_file(url) {
     2 
     3     var type = url.split('.'),
     4         file = type[type.length - 1];
     5 
     6     if (file == 'css') {
     7         var obj = document.createElement('link'),
     8             lnk = 'href',
     9             tp = 'text/css';
    10         obj.setAttribute('rel', 'stylesheet');
    11     } else {
    12         var obj = document.createElement('script'),
    13             lnk = 'src',
    14             tp = 'text/javascript'; 
    15     }
    16 
    17     obj.setAttribute(lnk, url);
    18     obj.setAttribute('type', tp);
    19     file == 'css' ? document.getElementsByTagName('head')[0].appendChild(obj) : document.body.appendChild(obj);
    20     return obj;
    21 
    22 };  

    考虑到js加载的特殊性,浏览器引擎在解析js时,对其他资源和文档都会停止。所以我们采用以上方法来异步加载js。而如果想给它增加 回调函数 呢?非IE下 onload 是完美支持的,IE下我们则用 onreadystatechange 事件监听 readyState 值变化。

     1  function loadjs(url, callback) {
     2 
     3     var elem = delay_file(url);
     4     var isIE = navigator.userAgent.indexOf('MSIE') == -1 ? false : true;
     5 
     6     if ( isIE ) {
     7         elem.onreadystatechange = function() {
     8             if (this.readyState && this.readyState == 'loading') return;
     9             if (callback) {
    10                 callback(); 
    11             }
    12         };
    13     } else {
    14         elem.onload = function() {
    15             if (callback) {
    16                 callback(); 
    17             }
    18         };  
    19     }
    20 
    21 }

    CSS的 load 事件

    CSS 的load事件跟以上讲的 onload 事件兼容性却是相反的,其他浏览器不支持 load 事件,在IE浏览器中反而是支持的。那怎么办呢?

    seajs给出了一个方案:

     1 function loadcss(url, callback) {
     2 
     3     var elem = delay_file(url);
     4 
     5     if (elem.attachEvent) {
     6         elem.attachEvent('onload', callback);
     7     } else {
     8         setTimeout(function() {
     9             poll(elem, callback);
    10         }, 0);
    11     }
    12 
    13     function poll(_elem, callback) {
    14 
    15         var isLoaded = false;
    16         var sheet = _elem['sheet'];
    17         var isOldWebKit = (navigator.userAgent.replace(/.*AppleWebKit/(d+)..*/, '$1')) * 1 < 536;
    18 
    19         if (isOldWebKit) { //webkit 版本小于 536
    20             if (sheet) {
    21                 isLoaded = true;
    22             }
    23         } else if (sheet) {
    24             try {
    25                 if (sheet.cssRules) {
    26                     isLoaded = true;
    27                 }
    28             } catch (ex) {
    29                 if (ex.code === 'NS_ERROR_DOM_SECURITY_ERR') {
    30                     isLoaded = true;
    31                 }
    32             }
    33         }
    34 
    35         if (isLoaded) {
    36             setTimeout(function() {
    37                 callback();
    38             }, 1);
    39         } else {
    40             setTimeout(function() {
    41                 poll(_elem, callback);
    42             }, 1);
    43         }
    44     }
    45 
    46 }

    貌似linkNode在加载前后 linkNode.sheet 和 linkNode.sheet.cssRules 的值会发生变化。我觉得还有一个方法虽然有点绕,但是也是最有效的方法:检测某个类名下的CSS属性是否存。

  • 相关阅读:
    国内乡村安防全面建设仍需推进,EasyNVR/EasyGBS/EasyCVR平台助力治安防控
    TSINGSEE青犀视频播放视频流的编码格式和封装格式有什么关系?
    全景摄像头怎么接入网页进行视频直播?
    EasyNTS编译报错could not read Username for XXXXX
    【LOJ#3005】Limited Memory
    【BZOJ2407】探险
    【洛谷P4480】餐巾计划问题
    【CF163E】e-Government
    【洛谷P4332】三叉神经树
    【洛谷P7518】宝石
  • 原文地址:https://www.cnblogs.com/zoucaitou/p/4170107.html
Copyright © 2011-2022 走看看