1. 程式人生 > 實用技巧 >JS紅寶書筆記——第二章 在HTML中使用JavaScript

JS紅寶書筆記——第二章 在HTML中使用JavaScript

1.<script>元素

向HTML頁面中插入JavaScript的主要方法,就是使用<script>元素。該元素由Netscape創造並在Netscape Navigator 2中首先實現。後來,這個元素被加入到正式的HTML規範中。HTML 4.01為<script>定義了下列6個屬性。

  • async:可選。表示應該立即下載指令碼,但不應妨礙頁面中的其他操作,比如下載其他資源或等待載入其他指令碼。只對外部指令碼檔案有效。
  • charset:可選。表示通過src屬性指定的程式碼的字符集。大多數瀏覽器會忽略它的值,因此該屬性很少有人用。
  • defer:可選。表示指令碼可以延遲到文件完全被解析和顯示之後再執行。只對外部指令碼檔案有效。IE7及更早版本對嵌入指令碼也支援這個屬性。
  • language:已廢棄。原來用於表示編寫程式碼使用的指令碼語言。大多數瀏覽器會忽略這個屬性,因此也幾乎沒有人用。
  • src:可選。表示包含要執行程式碼的外部檔案。
  • type:可選。可以看成是language的替代屬性,表示編寫程式碼使用的指令碼語言的內容型別(也稱為MIME型別)。雖然text/javascript和text/ecmascript都已經不被推薦使用,但人們一直以來使用的都還是text/javascript。實際上,伺服器在傳送JavaScript檔案時使用的MIME型別通常是application/x-javascript,但在type中設定這個值卻可能導致指令碼被忽略。另外,在非IE瀏覽器中還可以使用application/javascript和application/ecmascript。考慮到約定俗成和最大限度的瀏覽器相容性,目前type屬性的值依舊還是text/javascript。不過,這個屬性並不是必需的,如果沒有指定這個屬性,則其預設值仍為text/javascript。

使用<script>元素的方式有兩種:

  • 直接在頁面中嵌入JavaScript程式碼。
  • 包含外部JavaScript檔案。

在使用<script>元素嵌入JavaScript程式碼時,只須為<script>指定type屬性。然後把JavaScript程式碼直接放在元素內部即可:

<script type="text/javascript">
    function sayHi(){
    	alert("Hi!");
	}
</script>

包含在<script>元素內部的JavaScript程式碼將被從上至下依次解釋。在直譯器對<script>元素內部的所有程式碼求值完畢以前,頁面中的其餘內容都不會被瀏覽器載入或顯示。

在使用<script>嵌入JavaScript程式碼時,記住不要在程式碼中的任何地方出現"</script>"字串。因為按照解析嵌入式程式碼的規則,當瀏覽器遇到字串"</script>"時,就會認為那是結束的</script>標籤。而通過轉義字元“\”可以解決這個問題。

<script type="text/javascript">
    function sayHi(){
    	alert("<\/script>");//使用\轉義
	}
</script>

如果要通過<script>元素來包含外部JavaScript檔案,那麼src屬性就是必需的。這個屬性的值是一個指向外部JavaScript檔案的連結,例如:

<script type="text/javascript" src="example.js"></script>

外部檔案只須包含通常要放在開始的<script>和結束的</script>之間的那些JavaScript程式碼即可。與解析嵌入式JavaScript程式碼一樣,在解析外部JavaScript檔案(包括下載該檔案)時,頁面的處理也會暫時停止。

如果是在XHTML文件中,也可以省略前面示例程式碼中結束的</script>標籤,例如:

<script type="text/javascript" src="example.js" />

但是,不能在HTML文件使用這種語法。原因是這種語法不符合HTML規範,而且也得不到某些瀏覽器(尤其是IE)的正確解析。

按照慣例,外部JavaScript檔案帶有.js副檔名。但這個副檔名不是必需的,因為瀏覽器不會檢查包含JavaScript的檔案的副檔名。這樣一來,使用JSP、PHP或其他伺服器端語言動態生成JavaScript程式碼也就成為了可能。但是,伺服器通常還是需要看副檔名決定為響應應用哪種MIME型別。如果不使用.js副檔名,請確保伺服器能返回正確的MIME型別。

帶有src屬性的<script>元素不應該在其<script>和</script>標籤之間再包含額外的JavaScript程式碼。如果包含了嵌入的程式碼,則只會下載並執行外部指令碼檔案,嵌入的程式碼會被忽略。

通過<script>元素的src屬性還可以包含來自外部域的JavaScript檔案。<script>與<img>元素的src屬性都可以是指向當前HTML頁面所在域之外的某個域中的完整URL,例如:

<script type="text/javascript" src="http://somewhere.com/afile.js"></script>

利用這一點就可以在必要時通過不同的域來提供JavaScript檔案。不過,在訪問自己不能控制的伺服器上的JavaScript檔案時則要多加小心。如果想包含來自不同域的程式碼,則要麼你是那個域的所有者,要麼那個域的所有者值得信賴。

無論如何包含程式碼,只要不存在defer和async屬性,瀏覽器都會按照<script>元素在頁面中出現的先後順序對它們依次進行解析。

1.1 標籤的位置

按照傳統的做法,所有<script>元素都應該放在頁面的<head>元素中,這種做法的目的就是把所有外部檔案(包括CSS檔案和JavaScript檔案)的引用都放在相同的地方。但是在文件的<head>元素中包含所有JavaScript檔案,意味著必須等到全部JavaScript程式碼都被下載、解析和執行完成以後,才能開始呈現頁面的內容(瀏覽器在遇到<body>標籤時才開始呈現內容)。為了避免延遲期間的瀏覽器視窗中將是一片空白,現代Web應用程式一般都把全部JavaScript引用放在<body>元素中頁面內容的後面。

<!DOCTYPE html>
<html>
    <head>
        <title>Example HTML Page</title>
    </head>
    <body>
        <!--這裡放內容-->
        <script type="text/javascript" src="example1.js"></script>
        <script type="text/javascript" src="example2.js"></script>
    </body>
</html>

1.2 延遲指令碼

HTML 4.01為<script>標籤定義了defer屬性。這個屬性的用途是表明指令碼會被延遲到整個頁面都解析完畢後再執行。因此,在<script>元素中設定defer屬性,相當於告訴瀏覽器立即下載,但延遲執行。

<!DOCTYPE html>
<html>
    <head>
        <title>Example HTML Page</title>
        <script type="text/javascript" defer="defer" src="example1.js"></script>
        <script type="text/javascript" defer="defer" src="example2.js"></script>
    </head>
    <body>
        <!--這裡放內容-->
    </body>
</html>

在上面例子中,雖然把<script>元素放在了文件的<head>元素中,但其中包含的指令碼將延遲到瀏覽器遇到</html>標籤後再執行。HTML5規範要求指令碼按照它們出現的先後順序執行,因此第一個延遲指令碼會先於第二個延遲指令碼執行,而這兩個指令碼會先於DOMContentLoaded事件執行。在現實當中,延遲指令碼並不一定會按照順序執行,也不一定會在DOMContentLoaded事件觸發前執行,因此最好只包含一個延遲指令碼。

defer屬性只適用於外部指令碼檔案,這一點在HTML5中已經明確規定,因此支援HTML5的實現會忽略給嵌入指令碼設定的defer屬性。IE4~IE7還支援對嵌入指令碼的defer屬性,但IE8及之後版本則完全支援HTML5規定的行為。

把延遲指令碼放在頁面底部仍然是最佳選擇。

在XHTML文件中,要把defer屬性設定為defer="defer"。

1.3 非同步指令碼

HTML5為<script>元素定義了async屬性。這個屬性與defer屬性類似,都用於改變處理指令碼的行為。同樣與defer類似,async只適用於外部指令碼檔案,並告訴瀏覽器立即下載檔案。但與defer不同的是,標記為async的指令碼並不保證按照指定它們的先後順序執行。因此,確保兩者之間互不依賴非常重要。

指定async屬性的目的是不讓頁面等待兩個指令碼下載和執行,從而非同步載入頁面其他內容。為此,建議非同步指令碼不要在載入期間修改DOM。

非同步指令碼一定會在頁面的load事件前執行,但可能會在DOMContentLoaded事件觸發之前或之後執行。

在XHTML文件中,要把async屬性設定為async="async"。

1.4 在XHTML中的用法

HTML5正快速被前端開發人員採用,建議在學習和開發中遵循HTML5標準,本小節內容可跳過。

可擴充套件超文字標記語言,即XHTML(Extensible HyperText Markup Language),是將HTML作為XML的應用而重新定義的一個標準。編寫XHTML程式碼的規則要比編寫HTML嚴格得多,而且直接影響能否在嵌入JavaScript程式碼時使用<script>標籤。以下面的程式碼塊為例,雖然它們在HTML中是有效的,但在XHTML中則是無效的。

<script type="text/javascript">
	function compare(a, b){
        if (a < b){
            alert("A is less than B.")
        } else if (a > b){
            alert("A is greater than B.")
		}else{
            alert("A is equal to B.")
        }
    }
</script>

在HTML中,有特殊的規則用以確定<script>元素中的哪些內容可以被解析,但這些特殊的規則在XHTML中不適用。這裡比較語句a < b中的小於號(<)在XHTML中將被當作開始一個新標籤來解析。但是作為標籤來講,小於號後面不能跟空格,因此就會導致語法錯誤。

避免在XHTML中出現類似語法錯誤的方法有兩個。

一是用相應的HTML實體(<)替換程式碼中所有的小於號(<),替換後的程式碼類似如下所示:

<script type="text/javascript">
	function compare(a, b){
        if (a &lt; b){
            alert("A is less than B.")
        } else if (a > b){
            alert("A is greater than B.")
		}else{
            alert("A is equal to B.")
        }
    }
</script>

這樣可以讓程式碼在XHTML中正常執行,但卻導致程式碼不好理解了。

第二個方法,就是用一個CData片段來包含JavaScript程式碼。在XHTML(XML)中,CData片段是文件中的一個特殊區域,這個區域中可以包含不需要解析的任意格式的文字內容。因此,在CData片段中就可以使用任意字元,而且不會導致語法錯誤。引入CData片段後的JavaScript程式碼塊如下所示:

<script type="text/javascript"><! [CDATA[
	function compare(a, b){
        if (a &lt; b){
            alert("A is less than B.")
        } else if (a > b){
            alert("A is greater than B.")
		}else{
            alert("A is equal to B.")
        }
    }
]]></script>

在相容XHTML的瀏覽器中,這個方法可以解決問題。但實際上,還有不少瀏覽器不相容XHTML,因而不支援CData片段。怎麼辦呢?再使用JavaScript註釋將CData標記註釋掉就可以了:

<script type="text/javascript">
    //<! [CDATA[
	function compare(a, b){
        if (a &lt; b){
            alert("A is less than B.")
        } else if (a > b){
            alert("A is greater than B.")
		}else{
            alert("A is equal to B.")
        }
    }
//]]>
</script>

這種格式在所有現代瀏覽器中都可以正常使用。雖然有幾分hack的味道,但它能通過XHTML驗證,而且對XHTML之前的瀏覽器也會平穩退化。

在將頁面的MIME型別指定為"application/xhtml+xml"的情況下會觸發XHTML模式。並不是所有瀏覽器都支援以這種方式提供XHTML文件。

1.5 不推薦使用的語法

在最早引入<script>元素的時候,該元素與傳統HTML的解析規則是有衝突的。不支援JavaScript的瀏覽器(最典型的是Mosaic)會把<script>元素的內容直接輸出到頁面中,因而會破壞頁面的佈局和外觀。

Netscape與Mosaic協商並提出了一個解決方案,讓不支援<script>元素的瀏覽器能夠隱藏嵌入的JavaScript程式碼。這個方案就是把JavaScript程式碼包含在一個HTML註釋中,像下面這樣:

<script><!--
	function sayHi(){
        alert("Hi!");
    }
--></script>

給指令碼加上HTML註釋後,Mosaic等瀏覽器就會忽略<script>標籤中的內容;而那些支援JavaScript的瀏覽器在遇到這種情況時,則必須進一步確認其中是否包含需要解析的JavaScript程式碼。

雖然這種註釋JavaScript程式碼的格式得到了所有瀏覽器的認可,也能被正確解釋,但由於所有瀏覽器都已經支援JavaScript,因此也就沒有必要再使用這種格式了。在XHTML模式下,因為指令碼包含在XML註釋中,所以指令碼會被忽略。

2.嵌入程式碼與外部檔案

在HTML中嵌入JavaScript程式碼雖然沒有問題,但一般認為最好的做法還是儘可能使用外部檔案來包含JavaScript程式碼。不過,並不存在必須使用外部檔案的硬性規定,但使用外部檔案會有如下優點:

  • 可維護性高
  • 可快取
  • 適應未來(通過外部檔案來包含JavaScript無須使用前面提到XHTML或註釋hack。HTML和XHTML包含外部檔案的語法是相同的。)

3.文件模式

IE5.5引入了文件模式的概念,而這個概念是通過使用文件型別(doctype)切換實現的。最初的兩種文件模式是:混雜模式(quirks mode)和標準模式(standardsmode)。混雜模式會讓IE的行為與(包含非標準特性的)IE5相同,而標準模式則讓IE的行為更接近標準行為。雖然這兩種模式主要影響CSS內容的呈現,但在某些情況下也會影響到JavaScript的解釋執行。

之後,IE又提出一種所謂的準標準模式(almost standards mode)。這種模式下的瀏覽器特性有很多都是符合標準的,但也不盡然。不標準的地方主要體現在處理圖片間隙的時候(在表格中使用圖片時問題最明顯)。

如果在文件開始處沒有發現文件型別宣告,則所有瀏覽器都會預設開啟混雜模式。但採用混雜模式不是什麼值得推薦的做法,因為不同瀏覽器在這種模式下的行為差異非常大,如果不使用某些hack技術,跨瀏覽器的行為根本就沒有一致性可言。

對於標準模式,可以通過使用下面任何一種文件型別來開啟:

<!-- HTML 4.01 嚴格型 -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<!-- XHTML 1.0 嚴格型-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<!-- HTML 5 -->
<!DOCTYPE html>

而對於準標準模式,則可以通過使用過渡型(transitional)或框架集型(frameset)文件型別來觸發,如下所示:

<!-- HTML 4.01 過渡型 -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<!-- HTML 4.01 框架集型 -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">

<!-- XHTML 1.0 過渡型-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<!-- XHTML 1.0 框架集型-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">

準標準模式與標準模式非常接近,它們的差異幾乎可以忽略不計。因此,當有人提到“標準模式”時,有可能是指這兩種模式中的任何一種。而且,檢測文件模式時也不會發現什麼不同。後面章節提到標準模式時,指的是除混雜模式之外的其他模式。

4.<noscript>元素

<noscript>元素,用以在不支援JavaScript的瀏覽器中顯示替代的內容。這個元素可以包含能夠出現在文件<body>中的任何HTML元素(<script>元素除外)。包含在<noscript>元素中的內容只有在下列情況下才會顯示出來:

  • 瀏覽器不支援指令碼
  • 瀏覽器支援指令碼,但指令碼被禁用

小結

把JavaScript插入到HTML頁面中要使用<script>元素。使用這個元素可以把JavaScript嵌入到HTML頁面中,讓指令碼與標記混合在一起,也可以包含外部的JavaScript檔案。

需要注意的地方:

  • 在包含外部JavaScript檔案時,必須將src屬性設定為指向相應檔案的URL。而這個檔案既可以是與包含它的頁面位於同一個伺服器上的檔案,也可以是其他任何域中的檔案。
  • 所有<script>元素都會按照它們在頁面中出現的先後順序依次被解析。在不使用defer和async屬性的情況下,只有在解析完前面<script>元素中的程式碼之後,才會開始解析後面<script>元素中的程式碼。
  • 由於瀏覽器會先解析完不使用defer屬性的<script>元素中的程式碼,然後再解析後面的內容,所以一般應該把<script>元素放在頁面最後,即主要內容後面,</body>標籤前面。
  • 使用defer屬性可以讓指令碼在文件完全呈現之後再執行。延遲指令碼總是按照指定它們的順序執行。
  • 使用async屬性可以表示當前指令碼不必等待其他指令碼,也不必阻塞文件呈現。不能保證非同步指令碼按照它們在頁面中出現的順序執行。

另外,使用<noscript>元素可以指定在不支援指令碼的瀏覽器中顯示的替代內容。但在啟用了指令碼的情況下,瀏覽器不會顯示<noscript>元素中的任何內容。