前端优化方案——外部文件load事件

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

首先回顾下在onload事件以及DOMReady事件兼容方案中我们研究了页面文档加载和文档加载。最后我们用 window.onload 来触发下载完页面中所有物件;而对不支持DOMContentLoaded事件的浏览器则文档即DOM树则有考虑过采用检测 onreadystatechange 状态,在 IE 系列浏览器中,我们可以通过 onreadystatechange 监听元素加载状态,此方法是解决IE的第一方案,但是并不是完美方案(个人认为完美)。

iframe的 load 事件

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

// 事件监听兼容方案
function addEvent(elem,event,fn){
    if (elem.attachEvent) {
        elem.attachEvent('on'+event,fn)
    } else {
        elem.addEventListener(event,fn,false)
    }
}	

window.onload = function(){
	var iframeA = document.createElement('iframe');
	iframeA.src = 'http://www.baidu.com'
	addEvent(iframeA,'load',function(){
		document.body.bgColor = '#000'; // 回调函数
	});
	document.body.appendChild(iframeA);
}
		

优化页面建议不要嵌套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{width:200px;height:200px;}
#load{width:200px;color:#fff;text-align:center;background-color:#eee;}
		

js代码

// 封装通过ID获取
function $(id){
    return document.getElementById(id)
}

var isIE = navigator.appVersion.indexOf("MSIE") != -1 ? true: false;


// 监听flash是否加载成功
function listenMovie(flash){
    try{
        return Math.floor(flash.PercentLoaded()) == 100 ;
    }catch(e){
        return false;
    }
}

// 获取FLASH对象
function thisMovie(movieName) {
    if (isIE) {
        return window[movieName];
    }
    else {
        return document[movieName];
    }
}

// 创建flash
function createFlash(id,url){
	var html = '<object id="flash" height="200" width="200" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">'+
			   '<PARAM NAME="FlashVars" VALUE="">'+	
				'<PARAM NAME="Movie" VALUE="'+url+'">'+	
				'<PARAM NAME="WMode" VALUE="Transparent">'+	
				'<PARAM NAME="Quality" VALUE="High">'+	
				'<PARAM NAME="AllowScriptAccess" VALUE="always">'+	
				'<embed type="application/x-shockwave-flash" src="'+url+'" id="flashFF" name="flashFF" wmode="window" quality="high" width="200" height="200"></embed>'+
				'</object>';
	$(id).innerHTML = html;
}

window.onload = function(){
	createFlash('swfWrap','flips2.swf')
	var flashObj = isIE ? thisMovie("flash") : thisMovie("flashFF");
	var intervalID =  setInterval(function(){
		if (listenMovie(flashObj)) {
			clearInterval(intervalID);
			intervalID = null;
			$('load').innerHTML = 'flash加载完毕';
		}
	},60)
}
		

其中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 事件。研究网上的得出以下代码:

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

这里网上很多说法是这样子说的,只要加载过一次图片,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和CSS的 load ,首先准备以下添加js、css的函数:

function delay_file(url) {

    var type = url.split('.'),
        file = type[type.length - 1];

    if (file == 'css') {
        var obj = document.createElement('link'),
            lnk = 'href',
            tp = 'text/css';
        obj.setAttribute('rel', 'stylesheet');
    } else {
    	var obj = document.createElement('script'),
            lnk = 'src',
            tp = 'text/javascript';	
    }

    obj.setAttribute(lnk, url);
    obj.setAttribute('type', tp);
    file == 'css' ? document.getElementsByTagName('head')[0].appendChild(obj) : document.body.appendChild(obj);
    return obj;

};	
	

Js的 load 事件

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

function loadjs(url, callback) {

    var elem = delay_file(url);
    var isIE = navigator.userAgent.indexOf('MSIE') == -1 ? false : true;

    if ( isIE ) {
        elem.onreadystatechange = function() {
            if (this.readyState && this.readyState == 'loading') return;
            if (callback) {
    			callback();	
    		}
        };
    } else {
    	elem.onload = function() {
    		if (callback) {
    			callback();	
    		}
        };	
    }
    
}	
		

Css的 load 事件

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

seajs给出了一个方案,

function loadcss(url, callback) {

	var elem = delay_file(url);

	if (elem.attachEvent) {
		elem.attachEvent('onload', callback);
	} else {
		setTimeout(function() {
			poll(elem, callback);
		}, 0);
	}

	function poll(_elem, callback) {

		var isLoaded = false;
		var sheet = _elem['sheet'];
		var isOldWebKit = (navigator.userAgent.replace(/.*AppleWebKit\/(\d+)\..*/, '$1')) * 1 < 536;

		if (isOldWebKit) { //webkit 版本小于 536
			if (sheet) {
				isLoaded = true;
			}
		} else if (sheet) {
			try {
				if (sheet.cssRules) {
					isLoaded = true;
				}
			} catch (ex) {
				if (ex.code === 'NS_ERROR_DOM_SECURITY_ERR') {
					isLoaded = true;
				}
			}
		}

		if (isLoaded) {
			setTimeout(function() {
				callback();
			}, 1);
		} else {
			setTimeout(function() {
				poll(_elem, callback);
			}, 1);
		}
	}

}
		

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

好了,这次就说这些,以后遇到新的再继续更新。

本文源链接:http://www.html5jscss.com/link-file-load.html

转载请注明《前端优化方案——外部文件load事件》| html5jscss

评论关闭了.