熟悉CSS的開發者一定知道圖像替換技術,也深知它的意義,Dave Shea 曾在他的一篇文章對此做了詳細的總結,參看 Dave Shea’s Excellent summary ,Paul Young 在分析現存的所有方法的優缺點之後,提出了一種新的方法,並將其命名為“狀態域方法”(The State Method),本文將詳細介紹該方法的原理:
現存方法的缺點:
新的圖像替換方法:
新的圖像替換技術需要借助於js來實現,但很容易執行,只需要將一小段js引入到頭部即可。一旦JS執行,響應的規則前將附加“.image-on”,只要客戶端的圖片未被禁用,規則就會生效,下面是一條應用到h1“狀態域方法”的聲明:
h1 {
width: 100px;
height: 50px;
}
@media screen {
.images-on h1 {
text-indent: -10000px;
background-image: url(image.png);
overflow: hidden;
}
}
第一條規則總是生效,第二條只有在image未被禁用時生效。“text-indent”使文字偏移於屏幕之外,“overflow:hidden”主要用來在FF下放置錨點在被點擊時其焦點偏移於屏幕之外。
第二條規則包繞在@media screen中,主要用來保證圖像替換只發生在屏幕閱讀器中,而不是在打印狀態下執行。如果不這樣處理,頁面打印時,多數用戶將看到一個很大的空隙而不是有意義的文本。
該項技術執行起來很快。因為文本偏移於屏幕之外,圖像可以包含透明元素,透過圖像本身,你看不到任何文本。JS執行很快,幾乎是瞬時的,它充分利用浏覽器本身的特性。
方法解析
“狀態域方法”是在一種假定的狀態下,快速使CSS規則生效的方法,其上下文背景為document,這樣避免了浏覽器遍歷DOM樹。應用“狀態域方法”有兩個理由:
“狀態域方法”通過使用下面的script給Html附加一個class。
document.enableStateScope = function(scope, on)
{
var de = document.documentElement;
if (on)
de.className += " " + scope;
else
de.className = de.className.replace(
new RegExp("\\b" + scope + "\\b"), "");
};
這段JS有一點小問題,在示例頁中切換功能並不生效,我重新修改了一下,代碼如下:
function hasClass(ele,cls) {
return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
}
function addClass(ele,cls) {
if (!this.hasClass(ele,cls)) ele.className += " "+cls;
}
function removeClass(ele,cls) {
if (hasClass(ele,cls)) {
var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
ele.className=ele.className.replace(reg,' ');
}
}
document.enableStateScope = function(scope, on) {
var de = document.documentElement;
On ? addClass(de,scope) : removeClass(de,scope);
};
上面的hasClass、addClass、removeClass方法借用的是《Pro JavaScript Techniques》提供的方法。如果你使用過jquery,方法將更簡單。
“狀態域”可以通過下面的方法來切換:
if (condition == true) {
document.enableStateScope("myScope", true);
}
如果“狀態域”為“on”,狀態域的名字將附加到規則的選擇器之前,下面這條規則在條件為真時會將錨點的顏色變成blue。
a { color: red; }
.myScope a { color: blue; }
正如你所預想的那樣,狀態域圖像替代技術是通過檢查圖像是否被禁用而工作的。如果未被禁用,將激活“image-on”狀態域,這很直接了當。
檢查圖片是否禁用
該方法檢查圖片是否禁用,並不是請求服務器上的圖片,因為那樣會導致一次額外的http請求。作者創建了一個巧妙的方法。
在大多數浏覽器中,Image對象可以實例化並追溯到一個無效的URL(http://0),這樣很容易檢測Image的狀態。如果禁用,onerror事件將觸發,在JS文件的開頭,j建立一個新的圖像對象:
var img = new Image();
但是,有兩個古怪的浏覽器對此方法並不兼容。在Gecko浏覽器中,不論Image是否被禁用。Onerror事件總是被觸發。所幸的是,另外一種可行的方案可以解決此問題--給Html元素附加一個無效的背景圖片,然後通過getComputedStyle方法獲得style屬性。如果Image禁用,其屬性為none或url( invalid-url:):
if (img.style.MozBinding != null)
{
img.style.backgroundImage = "url(" + document.location.protocol + "//0)";
var bg = window.getComputedStyle(img, '').backgroundImage;
if (bg != "none" && bg != "url(invalid-url:)" || document.URL.substr(0, 2) == "fi")
{
document.enableStateScope("images-on", true);
}
}
另外一個富有挑戰性的浏覽器是safari,如果請求是一個無效的URL,safari的狀態欄將出現錯誤提示,但頁面布局不受任何影響。如果用戶的狀態欄處於開啟狀態,報錯將一直持續,這很不專業,同樣,作者研究了另外一種可行的方案。如果Image來自於1*1的gif圖像,且被數據編碼。如果Image禁用,其寬度將為0,以下為在safari中測試的情況:
else
{
img.style.CSSText = "-webkit-opacity:0";
if (img.style.webkitOpacity == 0)
{
img.onload = function()
{
document.enableStateScope("images-on", img.width > 0);
}
img.src =
"data:image/gif;base64," +
"R0lGODlhAQABAIAAAP///wAAACH5BAE" +
"AAAAALAAAAAABAAEAAAICRAEAOw==";
}
}
最後,對於其它浏覽器,在開始初始化Image對象時,僅僅需要測試onerror觸發事件。
else
{
img.onerror = function(e)
{
document.enableStateScope("images-on", true);
}
img.src = "about:blank";
}
狀態域是可以切換的
可以創建一個系統讓用戶在文本和替代圖像之間切換。
查看示例(示例文件由Paul Young提供)
class屬性添加到html之上而不是body或其它子元素之上,主要原因在於在圖像替換之前,body需要全面加載。如果“image-on”不添加到Html之上。當狀態域啟用時,將會出現閃動。
圖像替換技術是CSS中相當重要的一部分。鑒於現存圖像替換技術的缺點,作者花大量時間另辟蹊徑,方法獨到,值得借鑒。