熟悉css的开发者一定知道图像替换技术,也深知它的意义,Dave Shea 曾在他的一篇文章对此做了详细的总结,参看Dave Shea’s excellent summary,Paul Young在分析现存的所有方法的优缺点之后,提出了一种新的方法,并将其命名为“状态域方法”(The State Method),本文将详细介绍该方法的原理:
一、现存方法的缺点:
- 容易失效,例如:图像禁用或者替换图像含有透明区域;
- 过于复杂不能迅速可靠的执行;
- 通过js遍历文档树,在页面加载时出现的闪动不尽人意;
- 可能与一些浏览器不兼容;
二、新的图像替换方法:
新的图像替换技术需要借助于js来实现,但很容易执行,只需要将一小段js引入到头部即可。一旦js执行,响应的规则前将附加“.image-on”,只要客户端的图片未被禁用,规则就会生效,下面是一条应用到h1“状态域方法”的声明:
01.
h
1
{
02.
width
:
100px
;
03.
height
:
50px
;
04.
}
05.
@media
screen
{
06.
.images-on h
1
{
07.
text-indent
:
-10000px
;
08.
background-image
:
url
(image.png);
09.
overflow
:
hidden
;
10.
}
11.
}
第一条规则总是生效,第二条只有在image未被禁用时生效。“text-indent”使文字偏移于屏幕之外,“overflow:hidden”主要用来在FF下放置锚点在被点击时其焦点偏移于屏幕之外。
第二条规则包绕在@media screen中,主要用来保证图像替换只发生在屏幕阅读器中,而不是在打印状态下执行。如果不这样处理,页面打印时,多数用户将看到一个很大的空隙而不是有意义的文本。
该项技术执行起来很快。因为文本偏移于屏幕之外,图像可以包含透明元素,透过图像本身,你看不到任何文本。Js执行很快,几乎是瞬时的,它充分利用浏览器本身的特性。
三、方法解析
“状态域方法”是在一种假定的状态下,快速使css规则生效的方法,其上下文背景为document,这样避免了浏览器遍历DOM树。应用“状态域方法”有两个理由:
- 针对用户的反应,页面部分内容再格式化;
- 基于客户端浏览器、设备、和其它状况而附加额外的样式。
“状态域方法”通过使用下面的script给html附加一个class。
1.
document.enableStateScope =
function
(scope, on) {
2.
var
de = document.documentElement;
3.
if
(on)
4.
de.className +=
" "
+ scope;
5.
else
6.
de.className = de.className.replace(
new
RegExp(
"\\b"
+ scope +
"\\b"
),
""
);
7.
};
这段js有一点小问题,在示例页中切换功能并不生效,我重新修改了一下,代码如下:
01.
function
hasClass(ele,cls) {
02.
return
ele.className.match(
new
RegExp(
'(\\s|^)'
+cls+
'(\\s|$)'
));
03.
}
04.
function
addClass(ele,cls) {
05.
if
(!
this
.hasClass(ele,cls)) ele.className +=
" "
+cls;
06.
}
07.
function
removeClass(ele,cls) {
08.
if
(hasClass(ele,cls)) {
09.
var
reg =
new
RegExp(
'(\\s|^)'
+cls+
'(\\s|$)'
);
10.
ele.className=ele.className.replace(reg,
' '
);
11.
}
12.
}
13.
document.enableStateScope =
function
(scope, on) {
14.
var
de = document.documentElement;
15.
on ? addClass(de,scope) : removeClass(de,scope);
16.
};
上面的hasClass、addClass、removeClass方法借用的是《Pro JavaScript Techniques》提供的方法。如果你使用过jquery,方法将更简单。
“状态域”可以通过下面的方法来切换:
1.
if
(condition ==
true
) {
2.
document.enableStateScope(
"myScope"
,
true
);
3.
}
如果“状态域”为“on”,状态域的名字将附加到规则的选择器之前,下面这条规则在条件为真时会将锚点的颜色变成blue。
1.
a {
color
:
red
; }
2.
.myScope a {
color
:
blue
; }
正如你所预想的那样,状态域图像替代技术是通过检查图像是否被禁用而工作的。如果未被禁用,将激活“image-on”状态域,这很直接了当。
四、检查图片是否禁用
该方法检查图片是否禁用,并不是请求服务器上的图片,因为那样会导致一次额外的http请求。作者创建了一个巧妙的方法。
在大多数浏览器中,Image对象可以实例化并追溯到一个无效的URL(http://0),这样很容易检测Image的状态。如果Image可用,onerror事件将触发,在js文件的开头,建立一个新的图像对象:
1.
var
img =
new
Image();
但是,有两个古怪的浏览器对此方法并不兼容。在Gecko浏览器中,不论Image是否被禁用。Onerror事件总是被触发。所幸的是,另外一种可行的方案可以解决此问题--给html元素附加一个无效的背景图片,然后通过getComputedStyle方法获得style属性。如果Image禁用,其属性为none或url( invalid-url:):
1.
if
(img.style.MozBinding !=
null
) {
2.
img.style.backgroundImage =
"url("
+ document.location.protocol +
"//0)"
;
3.
var
bg = window.getComputedStyle(img,
''
).backgroundImage;
4.
if
(bg !=
"none"
&& bg !=
"url(invalid-url:)"
|| document.URL.substr(0, 2) ==
"fi"
){
5.
document.enableStateScope(
"images-on"
,
true
);
6.
}
7.
}
另外一个富有挑战性的浏览器是safari,如果请求是一个无效的URL,safari的状态栏将出现错误提示,但页面布局不受任何影响。如果用户的状态栏处于开启状态,报错将一直持续,这很不专业,同样,作者研究了另外一种可行的方案。如果Image来自于1*1的gif图像,且被数据编码。如果Image禁用,其宽度将为0,以下为在safari中测试的情况:
01.
else
{
02.
img.style.cssText =
"-webkit-opacity:0"
;
03.
if
(img.style.webkitOpacity == 0) {
04.
img.onload =
function
() {
05.
document.enableStateScope(
"images-on"
, img.width > 0);
06.
}
07.
img.src =
"data:image/gif;base64,"
+
"R0lGODlhAQABAIAAAP///wAAACH5BAE"
+
"AAAAALAAAAAABAAEAAAICRAEAOw=="
;
08.
}
09.
}
最后,对于其它浏览器,在开始初始化Image对象时,仅仅需要测试onerror触发事件。
1.
else
{
2.
img.onerror =
function
(e) {
3.
document.enableStateScope(
"images-on"
,
true
);
4.
}
5.
img.src =
"about:blank"
;
6.
}
五、状态域是可以切换的
可以创建一个系统让用户在文本和替代图像之间切换。
class属性添加到html之上而不是body或其它子元素之上,主要原因在于在图像替换之前,body需要全面加载。如果“image-on”不添加到html之上。当状态域启用时,将会出现闪动。
图像替换技术是css中相当重要的一部分。鉴于现存图像替换技术的缺点,作者花大量时间另辟蹊径,方法独到,值得借鉴。
实例:tate-scope-image-replacement.zip
原文地址:http://www.sitepoint.com/article/image-replacement-state-scope/
转载地址:http://www.denisdeng.com/?p=235
作者简介:Paul Young,Skybound 软件创始人之一,缔创了视觉化css编辑器。