图像替换是网站前端设计中经常使用的技巧之一。本文在pFIR图像替换技术的基础上,提出了一个改进版本。
什么是图像替换(FIR)?
考虑这样一种情况:<h4>Image Replacement</h4>
,效果:
Image Replacement
现在希望用一张图片替换掉文字内容,使得网页更加丰富多彩(很多情况下是为了表现出特殊字体效果)。做到这种效果:
PerfectWorks大牛的blog介绍了常见的几种FIR技术:谈谈CSS图像替换技术(FIR)。
PerfectWorks认为,图像替换技术中最难解决的问题是有些用户关闭了图像显示、而浏览器支持CSS——某些FIR解决方案在这种情况下会造成图片不显示、而原来的文字也不见了。
pFIR的缺陷
PerfectWorks也提出了他自己的一种图像替换技术:用JavaScript实现完美图像替换(pFIR)
但是,pFIR有几个缺陷:
- 不支持IE5.5:
document.getElementsByTagName('*')
返回空数组 - 不支持Opera:关闭图片显示时,IMG的load事件仍然会发生,造成误判
- 在各版本IE工作不稳定:bug重现方法——
- IE的图像显示功能处于默认的打开状态
- 访问example2.html。现在可以观察到图像替换正常工作。
- 鼠标点击地址栏,然后按ENTER键(注意不能刷新)。此时图像替换不再工作。
- 比较慢的网络环境下会造成闪烁:背景图片尚未下载完毕,文字已经弹开;并且,默认情况下浏览器会在稍后下载背景图片、而先下载IMG标签引用的图片,这更会加剧这个问题
pFIR的改进 pFIR_improved
- 支持IE5.5:当document.getElementsByTagName('*')返回空数组时,对各个HTML 4.01定义的标签名称依次调用getElementsByTagName并处理
- 支持Opera:在Opera中使用宽度判断
- 解决IE不刷新bug:该bug的原因是IE对从缓存中读取的图片,不会触发load事件;此时只能判断宽度(已更新ImageSupport函数)
- 比较慢的网络环境下减少闪烁:遍历CSS规则表,对可能相关的规则中background-image引用的图片进行预载入
function pFIR_improved(className,className2) {
ImageSupport(function(){//仅当打开图像显示时执行图像替换
var d=null;
var z=0;//尚未完成预载入的图片数量
var done=false;
var g=function() {
if (--z>0) return;//又载入了一幅图片,全部载入了吗?
if (done) return; done=true;
if (d) document.body.removeChild(d);
var c1=' '+className+' ',c2=' '+className2;
var r=function(E) {
for (var i=0,ilen=E.length;i<ilen;++i) {
var e=E[i];
if ((' '+e.className+' ').indexOf(c1)>=0) {
e.className+=c2;//执行图像替换
}
}
}
var E=document.getElementsByTagName('*');
if (!E || E.length<1) {
var HTML=['A','ABBR','ACRONYM','ADDRESS','APPLET','AREA','B','BASE','BASEFONT','BDO',
'BIG','BLOCKQUOTE','BODY','BR','BUTTON','CAPTION','CENTER','CITE','CODE','COL',
'COLGROUP','DD','DEL','DFN','DIR','DIV','DL','DT','EM','FIELDSET','FONT','FORM',
'FRAME','FRAMESET','H1','H2','H3','H4','H5','H6','HEAD','HR','HTML','I','IFRAME',
'IMG','INPUT','INS','ISINDEX','KBD','LABEL','LEGEND','LI','MAP','MENU','META',
'NOFRAMES','NOSCRIPT','OBJECT','OL','OPTGROUP','OPTION','P','PARAM','PRE','Q','S',
'SAMP','SELECT','SMALL','SPAN','STRIKE','STRONG','SUB','SUP','TABLE','TBODY','TD',
'TEXTAREA','TFOOT','TH','THEAD','TITLE','TR','TT','U','UL',
'VAR'];//HTML 4.01 elements except LINK,SCRIPT,STYLE
for (var i=0,ilen=HTML.length;i<ilen;++i) r(document.getElementsByTagName(HTML[i]));
} else {
r(E);
}
};
try{
d=document.createElement('div');//包含预载入图片的容器
d.style.position='absolute';
d.style.visibility='hidden';
d.style.zIndex='-1';
var P={};
var STYLE=document.styleSheets;
var c2='.'+className2;
for (var i=0,ilen=STYLE.length;i<ilen;++i) {//遍历所有样式表
var style=STYLE[i];
var R=style.cssRules;
if (!R) R=style.rules;
for (var j=0,jlen=R.length;j<jlen;++j) {//遍历所有样式规则
var r=R[j];
var bg=r.style.backgroundImage;
if ((r.selectorText).indexOf(c2)<0 || !bg || bg=='') continue;
//找到selector包含className2、并定义了background-image属性的样式规则
var m=bg.match(/url\(["']?([^\("'\)]+)["']?\)/);//取出background-image属性中的URL地址
if (!m) continue;
var im=m[1];
if (im.substr(0,5)=='data:') continue;
if (P[im]) continue;//已经开始预载入了
P[im]=true;
var img=document.createElement('img');//预载入图片
img.src=m[1];
if (img.width<20) {
img.onload=g;
++z;
}//已缓存时能马上取得width,未缓存时width为0
d.appendChild(img);
}
}
document.body.appendChild(d);
if (z<=0) g();//没有图像需要预载入,直接执行替换
else setTimeout(function(){z=-4;g();},4000);//如果4秒仍未完成预载入,仍然执行图像替换
}catch(ex){
d=null;
g();//出现错误(可能是不支持样式表遍历),直接执行替换
}
}); }
以上代码兼容的浏览器:Firefox3,Firefox2,Opera9,Chrome,IE8,IE7,IE5.5
pFIR_improved的局限性
- 由于预载入需要时间,使图像替换得较晚,用户会看到未替换的原始文字。
- 如果预载入超时(4秒仍未完成),还是会有闪烁现象。
- 代码经JSMin处理后长达2.6k,是pFIR的四倍;去掉HTML标签列表(不支持IE5.5)也达到pFIR的三倍。