標簽的方方面面


<img>標簽是頁面上最為重要的元素之一。很難想象一個頁面上沒有圖片的樣子,這樣的頁面效果將會大打折扣。


任何一個前端工程師想必對<img>標簽都非常熟悉了,畢竟經常和它打交道嘛。但你真的對它完全了解嗎?如果你能准確無誤地回答出以下幾個關於<img>的問題,那么恭喜你,本文你可以不再往下看了,或者說你可以用省視的目光來核對本文。

  • 問題1:如果在一個頁面上插入<img>標簽,有哪些屬性是必需的?

  • 問題2:<img>標簽在HTML和XHTML中有什么區別?

  • 問題3:在一個頁面上插入<img>標簽,為什么說最好要使用height和width屬性?

  • 問題4:<img>標簽的onload/onerror/onabort事件,在什么情況下會被觸發?

  • 問題5:我們一般知道,當一個圖片請求返回404時,會觸發onerror事件,那當圖片請求返回302時,會觸發onerror事件嗎?304呢?403呢?500呢?請求超時呢?甚至說當返回200,但內容並非是圖片時,也會觸發onerror么?

  • 問題6:圖片觸發onerror事件時,能使用javascript獲取到圖片請求的響應代碼么?

  • 問題7:我們一般知道,<img>標簽可以用來發起跨域請求,你能手寫出一段正確使用<img>發起跨域請求的javascript代碼么?

  • 問題8:用戶是可以設置瀏覽器不顯示圖片的,尤其是在移動設備上,用戶為了節省流量,往往會進行那么,如何獲知用戶是否禁止瀏覽圖片呢?

 


問題已經提出,你能回答出哪幾個呢?事實上,在沒有查看標准和親自做試驗之前,筆者對其中一些問題的答案也是模棱兩可,不敢確保自己的答案100%准確。

下面,我們逐一分析和解答上述的8個問題:

問題1:如果在一個頁面上插入<img>標簽,有哪些屬性是必需的?

答案是src和alt。

實例:(本實例摘自W3school: http://www.w3school.com.cn

<img src=" http://www.w3school.com.cn/i/eg_tulip.jpg"  alt="上海鮮花港 - 郁金香" />

 

src屬性規定了顯示圖像的URL,瀏覽器會對該URL發起Http Get請求。

alt屬性則規定了圖像的替代文本,在圖像無法顯示或者用戶禁用圖像顯示時,代替圖像顯示在瀏覽器中的內容。如下圖:

與src屬性相比較,alt屬性更容易被設計人員和開發人員所忽視,事實上,在筆者撰寫本文時,即使在國內一些大型門戶網站首頁上(例如新浪、搜狐),我們也可以找到許多沒有alt屬性的<img>標簽。但筆者強烈推薦在文檔的每個圖像中都使用這個屬性,這樣即使圖像無法顯示,用戶還是可以看到的一些相關信息,從而大大提高了頁面的用戶友好性。

問題2:<img>標簽在HTML和XHTML中有什么區別?

(答案摘自W3school: http://www.w3school.com.cn

在 HTML 中,<img> 標簽沒有結束標簽。例如:

<img src=" http://www.w3school.com.cn/i/eg_tulip.jpg"  alt="上海鮮花港 - 郁金香" >

 

在 XHTML 中,<img> 標簽必須被正確地關閉。

<img src=" http://www.w3school.com.cn/i/eg_tulip.jpg"  alt="上海鮮花港 - 郁金香" />

 

在 HTML 4.01 中,不推薦使用 image 元素的 "align"、"border"、"hspace" 以及 "vspace" 屬性。在 XHTML 1.0 Strict DTD 中,不支持 image 元素的 "align"、"border"、"hspace" 以及 "vspace" 屬性。

 問題3:在一個頁面上插入<img>標簽,為什么說最好要使用height和width屬性?

您在瀏覽網頁的時候,可能會遇到這種情況:隨着頁面中的圖像加載並成功顯示,頁面上的內容會隨之發生不規律的移動,影響您的閱讀。這種情況就是因為頁面上的圖像沒有定義height和width屬性而導致的。

如果沒有定義圖片的height和width屬性,那么瀏覽器為了能夠顯示每一個加載的圖像,它需要先下載圖像,然后解析出圖像的高度和寬度,並在顯示窗口留出相應的屏幕空間,這樣就會導致瀏覽器不斷地重新計算/調整頁面的布局,這可能會延遲文檔的顯示,並導致頁面重繪。

因此,筆者建議使用<img>的 height 和 width 屬性來指定圖像的尺寸。這樣的話,瀏覽器在下載圖像之前就為其預留出了空間,從而可以加速文檔的顯示,還可以避免文檔內容的移動而引起頁面重繪。

但是,需要注意的是:不要通過 height 和 width 屬性來縮放圖像。如果通過 height 和 width 屬性來縮小圖像,那么用戶就必須下載大容量的圖像(即使圖像在頁面上看上去很小)。正確的做法是,在網頁上使用圖像之前,應該通過軟件把圖像處理為合適的尺寸。當然,這個准則在實際應用中也有例外,例如筆者就認為,小比例的圖像縮放應該是允許的,此外,如果頁面上需要加載同一張圖像的不同尺寸的顯示,因為瀏覽器對同一個圖像只會請求一次,因此此時就建議使用height 和 width 屬性來縮放圖像。

問題4:<img>標簽的onload/onerror/onabort事件,在什么情況下會被觸發?

onload: 事件會在圖像加載完成后立即發生。

onerror: 事件會在文檔或圖像加載過程中發生錯誤時被觸發。

onabort: 事件會在圖像加載被中斷時發生。例如用戶單擊了瀏覽器的Stop按鈕,或者在圖像下載的過程中。

 

上面的三句話雖然看起來很簡單,但實際上有許多細節需要進一步的研究,尤其是onload和onerror事件。這些細節的問題,將在問題5中提出。

問題5:我們一般知道,當一個圖片請求返回404時,會觸發onerror事件,那當圖片請求返回302時,會觸發onerror事件嗎?304呢?403呢?500呢?請求超時呢?甚至說當返回200,但內容並非是圖片時,也會觸發onerror么?

這些問題需要動手做個試驗。試驗的結果如下表所示:

圖片請求

觸發的事件類型

IE

FireFox

Chrome

返回404

onerror

返回302,並且跳轉地址為一個正常的圖片

onload

所觸發的事件類型與原始的請求無關,而是與跳轉地址相關。

返回304,並且緩存生效

onload

但也要注意,如果緩存不存在,僅僅是單純地返回304,依然會觸發onerror

返回403

onerror

返回500

onerror

請求超時

onerror

返回504

返回200,但返回的內容並非圖片

onerror

 

問題6:圖片觸發onerror事件時,能使用javascript獲取到圖片請求的響應代碼么?

很遺憾,目前瀏覽器廠商尚未提供相關的接口。 

問題7:我們一般知道,<img>標簽可以用來發起跨域請求,你能手寫出一段正確使用<img>發起跨域請求的javascript函數么?

這個問題看起來很簡單,或許你很快的就寫出了以下代碼:

 1 function setImageSrc() {
 2     var i = new Image();
 3     i.src = "http://.../1.gif";
 4     i.onload = function() {
 5         // do sth.
 6     };
 7 
 8     i.onerror = function() {
 9         // do sth.
10     }
11 
12     i.onabort = function() {
13         // do sth.
14     }
15 }

代碼中新建了一個image對象,並綁定了onload, onerror, onabort三個事件處理函數。

但實際上,上述代碼存在幾個問題,你能看出幾個呢?

1)       屬性src賦值操作應該在事件綁定之后:否則,有可能出現圖片已經加載完畢、但事件綁定尚未完成的情況。例如,在上述代碼片段中,如果在第三行和第四行之間增加一句alert(1),就能重現這種情況。

2)       在IE6中,上述事件綁定代碼會形成一個循環引用——Image對象的onload屬性引用了一個匿名函數對象,而匿名函數通過其作用域鏈引用會Image對象,這種循環引用會在IE6中導致內存泄露。因此,在onload的匿名函數中,應該解除循環引用,正確的代碼類似於:

1 i.onload = function() {
2     // do sth.
3     
4     i.onload = null;
5     i = null;
6 }

 

3)       在IE6中,如果圖片是多幀的gif,會觸發多次的onload事件。因此,為避免這種情況,也需要在onload事件處理函數中解除事件函數:

1 i.onload = function() {
2     // do sth.
3 
4     i.onload = null;
5     i = null;
6 }

問題8:用戶是可以設置瀏覽器不顯示圖片的,尤其是在移動設備上,用戶為了節省流量,往往會禁止圖片顯示。那么,如何獲知用戶是否禁止瀏覽圖片呢?

注:該問題的解決方案來源於 http://stackoverflow.com/questions/8379156/how-to-detect-if-images-are-disabled-in-browser,筆者對其中的原理和代碼bug做了相應的解讀和修復。

在Firefox和Chrome中,可以使用Image對象的complete屬性來解決此問題:設置Image對象的src屬性,以請求一個不存在的圖片,當瀏覽器禁止顯示圖片時,Image對象的complete屬性為true,否則為false。

在Opera中,也可以使用Image對象的complete屬性,但它與Firefox和Chrome的不同,設置Image的src后,在onload之前,它一直顯示為false。但我們可以將圖片的src設置為一個特殊的值:img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";  這樣,當Opera禁止顯示圖片時,Image的complete屬性為false,否則為true。

而在IE中,Image的complete屬性會一直為false。因此,但我們注意到,當IE禁止顯示圖片時,是不會觸發Image對象的onload/onerror/onabort事件的。針對該特性,我們使用setTimeout函數,當在一定的時間內沒有檢測onload/onerror/onabort事件的發生,則認為瀏覽器禁止顯示圖片。

具體的代碼如下:

 

 1 <script type="text/javascript">
 2         detectImageEnabledMode({
 3             onDetectImageIsDisabled:function(){
 4                 alert('disabled');
 5             },
 6             onDetectImageIsEnabled:function(){
 7                 alert('enabled');
 8             }
 9         });
10 
11         // 用來檢測瀏覽器是否禁止顯示圖片
12      // 參數options: onDetectImageIsDisabled,檢測到禁止顯示圖片后的回調函數;onDetectImageIsEnabled, 檢測到顯示圖片后的回調函數
13      function detectImageEnabledMode(options){
14             /* define disabled/enabled actions */
15             var actionCounter = 0;
16             var enabledAction = options.onDetectImageIsEnabled || function() {};
17             var enaledActionRun = function(){
18                 if(actionCounter) 
19                     return;
20                 actionCounter++;
21                 enabledAction();
22             }
23 
24             var disabledAction = options.onDetectImageIsDisabled || function(){};
25             var disabledActionRun = function(){
26                 if(actionCounter) return;
27                 actionCounter++;
28                 disabledAction();
29             }        
30             
31             /* create image */
32             var img = new Image();
33             var currentTime = (+new Date);
34             if(navigator.appName.indexOf('Microsoft Internet Explorer') != -1){// ie
35                 img.onload = i.onerror = i.onabort = enaledActionRun;
36 
37                 // 試圖訪問一個不存在的圖片
38                 img.src = currentTime+'.'+currentTime+'?time='+currentTime;
39                 // 如果500毫秒后,尚未觸發圖片的onload/onerror/onabort事件,則認為瀏覽器禁止了圖片顯示
40                 setTimeout(function(){
41                     disabledActionRun();
42                 }, 500);
43             }else if (navigator.appName.indexOf('Opera') != -1) {// opera
44                 img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="+'?time='+currentTime;
45 
46                 // 禁止圖片顯示時,img.complete==false
47                 if(img.complete){ 
48                     enaledActionRun();
49                 }else{
50                     disabledActionRun();
51                 }
52             }else{// firefox chrome safari
53                 // 試圖訪問一個不存在的圖片
54                 img.src = currentTime+'.'+currentTime+'?time='+currentTime;
55 
56                 // 禁止圖片顯示時,img.complete==true
57                 if(img.complete){
58                     disabledActionRun();
59                 }else{
60                     enaledActionRun();
61                 }
62             }
63         }
64     </script>

至此,相信你已經對Image有了更進一步的了解了。事實上,還有與Image相關的許多技巧在本文沒有提及,例如Image的lazyload。大家在實際編程中可以慢慢接觸和了解。在以后的文章中,我也會根據實際情況持續地寫一些與Image相關的文章。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM