<p>
向 HTML 頁面中插入 JavaScript 的主要方法,就是使用<script>元素。這個元素由 Netscape 創造并在 Netscape Navigator 2 中首先實現。后來,這個元素被加入到正式的 HTML 規范中。 HTML 4.01 為<script>定義了下列 6 個屬性。
</p>
<ul>
<li>
async:可選。表示應該立即下載腳本,但不應妨礙頁面中的其他操作,比如下載其他資源或等待加載其他腳本。只對外部腳本文件有效。
</li>
<li>
charset:可選。表示通過 src 屬性指定的代碼的字符集。由于大多數瀏覽器會忽略它的值,因此這個屬性很少有人用。<br />
</li>
<li>
defer:可選。表示腳本可以延遲到文檔完全被解析和顯示之后再執行。只對外部腳本文件有效。 <code>IE7</code> 及更早版本對嵌入腳本也支持這個屬性。
</li>
<li>
language:已廢棄。原來用于表示編寫代碼使用的腳本語言(如 JavaScript、JavaScript1.2或 VBScript)。大多數瀏覽器會忽略這個屬性,因此也沒有必要再用了。
</li>
<li>
src:可選。表示包含要執行代碼的外部文件。
</li>
<li>
type:可選。可以看成是 language 的替代屬性;表示編寫代碼使用的腳本語言的內容類型(也稱為 MIME 類型)。雖然 text/javascript 和 text/ecmascript 都已經不被推薦使用,但人們一直以來使用的都還是 text/javascript。實際上,服務器在傳送 JavaScript 文件時使用的MIME 類型通常是 application/x–javascript,但在 type 中設置這個值卻可能導致腳本被忽略。另外,在非 IE瀏覽器中還可以使用以下值:<code>application/javascript</code> 和 <code>application/ecmascript</code>。考慮到約定俗成和最大限度的瀏覽器兼容性,目前 type 屬性的值依舊還是text/javascript。不過,這個屬性并不是必需的,如果沒有指定這個屬性,則其默認值仍為text/javascript。
</li>
<li>
使用<script>元素的方式有兩種:直接在頁面中嵌入 JavaScript 代碼和包含外部 JavaScript文件。
</li>
</ul>
<p>
在使用<script>元素嵌入 JavaScript 代碼時,只須為<script>指定 type 屬性。然后,像下面這樣把 JavaScript 代碼直接放在元素內部即可:
</p>
<pre class="runsl"><script type="text/javascript">
function sayHi() {
alert("Hi!");
}
</script></pre>
<p>
包含在<script>元素內部的 JavaScript 代碼將被從上至下依次解釋。就拿前面這個例子來說,解釋器會解釋一個函數的定義,然后將該定義保存在自己的環境當中。在解釋器對<script>元素內部的所有代碼求值完畢以前,頁面中的其余內容都不會被瀏覽器加載或顯示。在使用<code><script></code>嵌入 JavaScript 代碼時,記住不要在代碼中的任何地方出現"<code></script></code>"字符串。
</p>
<p>
例如,瀏覽器在加載下面所示的代碼時就會產生一個錯誤:
</p>
<pre class="runsl"><script type="text/javascript">
function sayScript() {
alert("</script>");
}
</script></pre>
<p>
因為按照解析嵌入式代碼的規則,當瀏覽器遇到字符串"<code></script></code>"時,就會認為那是結束的<code></script></code>標簽。而通過轉義字符“<code>/</code>”可以解決這個問題,例如:
</p>
<pre class="runsl"><script type="text/javascript">
function sayScript() {
alert("<\/script>");
}
</script></pre>
<p>
</p>
<p>
這樣寫代碼瀏覽器可以接受,因而也就不會導致錯誤了。
</p>
<p>
如果要通過<script>元素來包含外部 JavaScript 文件,那么 src 屬性就是必需的。這個屬性的值是一個指向外部 JavaScript 文件的鏈接,例如:
</p>
<pre><script type="text/javascript" src="example.js"></script></pre>
<p>
在這個例子中,外部文件 example.js 將被加載到當前頁面中。外部文件只須包含通常要放在開始的<script>和結束的</script>之間的那些 JavaScript 代碼即可。與解析嵌入式 JavaScript 代碼一樣,在解析外部 JavaScript 文件(包括下載該文件)時,頁面的處理也會暫時停止。如果是在 XHTML 文檔中,也可以省略前面示例代碼中結束的</script>標簽,例如:
</p>
<pre><script type="text/javascript" src="example.js" /></pre>
<p>
但是,不能在 HTML 文檔使用這種語法。原因是這種語法不符合 HTML 規范,而且也得不到某些瀏覽器(尤其是 IE)的正確解析。
</p>
<p>
</p>
<blockquote>
按照慣例,外部 JavaScript 文件帶有.js 擴展名。但這個擴展名不是必需的,因為瀏覽器不會檢查包含 JavaScript 的文件的擴展名。這樣一來,使用 JSP、 PHP 或其他服務器端語言動態生成 JavaScript 代碼也就成為了可能。但是,服務器通常還是需要看擴展名決定為響應應用哪種 MIME 類型。如果不使用.js 擴展名,請確保服務器能返回正確的 MIME 類型。
</blockquote>
<p>
</p>
<p>
需要注意的是,帶有 src 屬性的<script>元素不應該在其<script>和</script>標簽之間再包含額外的 JavaScript 代碼。如果包含了嵌入的代碼,則只會下載并執行外部腳本文件,嵌入的代碼會被忽略。
</p>
<p>
另外,通過<script>元素的 src 屬性還可以包含來自外部域的 JavaScript 文件。這一點既讓<script>元素倍顯強大,又讓它備受爭議。在這一點上, <script>與<img>元素非常相似, 即它的 src屬性可以是指向當前 HTML 頁面所在域之外的某個域中的完整 URL,例如:
</p>
<pre><script type="text/javascript" src="http://www.somewhere.com/afile.js"></script></pre>
<p>
這樣,位于外部域中的代碼也會被加載和解析,就像這些代碼位于加載它們的頁面中一樣。利用這一點就可以在必要時通過不同的域來提供 JavaScript 文件。不過,在訪問自己不能控制的服務器上的JavaScript 文件時則要多加小心。如果不幸遇到了懷有惡意的程序員,那他們隨時都可能替換該文件中的代碼。因此,如果想包含來自不同域的代碼,則要么你是那個域的所有者,要么那個域的所有者值得信賴。
</p>
<p>
無論如何包含代碼,只要不存在 defer 和 async 屬性,瀏覽器都會按照<script>元素在頁面中出現的先后順序對它們依次進行解析。換句話說,在第一個<script>元素包含的代碼解析完成后,第二個<script>包含的代碼才會被解析,然后才是第三個、第四個……
</p>
<h2>
2.1.1 標簽的位置
</h2>
<p>
按照傳統的做法,所有<script>元素都應該放在頁面的<head>元素中,例如:
</p>
<pre><!DOCTYPE html>
<html>
<head>
<title>
Example HTML Page
</title>
<script type="text/javascript" src="example1.js"></script>
<script type="text/javascript" src="example2.js"></script>
</head>
<body>
<!-- 這里放內容 -->
</body>
</html></pre>
<p>
這種做法的目的就是把所有外部文件(包括 CSS 文件和 JavaScript 文件)的引用都放在相同的地方。可是,在文檔的<head>元素中包含所有 JavaScript 文件,意味著必須等到全部 JavaScript 代碼都被下載、解析和執行完成以后,才能開始呈現頁面的內容(瀏覽器在遇到<body>標簽時才開始呈現內容)。對于那些需要很多 JavaScript 代碼的頁面來說,這無疑會導致瀏覽器在呈現頁面時出現明顯的延遲,而延遲期間的瀏覽器窗口中將是一片空白。為了避免這個問題,現代 Web 應用程序一般都把全部 JavaScript 引用放在<body>元素中頁面內容的后面,如下例所示:
</p>
<pre><!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></pre>
<p>
這樣,在解析包含的 JavaScript 代碼之前,頁面的內容將完全呈現在瀏覽器中。而用戶也會因為瀏覽器窗口顯示空白頁面的時間縮短而感到打開頁面的速度加快了。
</p>
<h2>
2.1.2 延遲腳本
</h2>
<p>
HTML 4.01 為<script>標簽定義了 defer 屬性。這個屬性的用途是表明腳本在執行時不會影響頁面的構造。也就是說,腳本會被延遲到整個頁面都解析完畢后再運行。因此,在<script>元素中設置defer 屬性,相當于告訴瀏覽器立即下載,但延遲執行。
</p>
<pre><!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></pre>
<p>
在這個例子中,雖然我們把<script>元素放在了文檔的<head>元素中,但其中包含的腳本將延遲到瀏覽器遇到</html>標簽后再執行。 HTML5 規范要求腳本按照它們出現的先后順序執行,因此第一個延遲腳本會先于第二個延遲腳本執行,而這兩個腳本會先于 DOMContentLoaded 事件 (詳見第 13 章)執行。在現實當中,延遲腳本并不一定會按照順序執行,也不一定會在 DOMContentLoaded 事件觸發前執行,因此最好只包含一個延遲腳本。
</p>
<p>
前面提到過, defer 屬性只適用于外部腳本文件。這一點在 HTML5 中已經明確規定,因此支持HTML5 的實現會忽略給嵌入腳本設置的 defer 屬性。 IE4~IE7 還支持對嵌入腳本的 defer 屬性,但IE8 及之后版本則完全支持 HTML5 規定的行為。
</p>
<p>
IE4、 Firefox 3.5、 Safari 5 和 Chrome 是最早支持 defer 屬性的瀏覽器。其他瀏覽器會忽略這個屬性,像平常一樣處理腳本。為此,把延遲腳本放在頁面底部仍然是最佳選擇。
</p>
<p>
</p>
<blockquote>
在 XHTML 文檔中,要把 defer 屬性設置為 defer="defer"。
</blockquote>
<p>
</p>
<h2>
2.1.3 異步腳本
</h2>
<p>
HTML5 為<script>元素定義了 async 屬性。這個屬性與 defer 屬性類似,都用于改變處理腳本的行為。同樣與 defer 類似, async 只適用于外部腳本文件,并告訴瀏覽器立即下載文件。但與 defer不同的是,標記為 async 的腳本并不保證按照指定它們的先后順序執行。例如:
</p>
<pre><!DOCTYPE html>
<html>
<head>
<title>
Example HTML Page
</title>
<script type="text/javascript" async src="example1.js"></script>
<script type="text/javascript" async src="example2.js"></script>
</head>
<body>
<!-- 這里放內容 -->
</body>
</html></pre>
<p>
在以上代碼中,第二個腳本文件可能會在第一個腳本文件之前執行。因此,確保兩者之間互不依賴非常重要。指定 async 屬性的目的是不讓頁面等待兩個腳本下載和執行,從而異步加載頁面其他內容。為此,建議異步腳本不要在加載期間修改 DOM。
</p>
<p>
異步腳本一定會在頁面的 load 事件前執行,但可能會在 DOMContentLoaded 事件觸發之前或之后執行。支持異步腳本的瀏覽器有 Firefox 3.6、 Safari 5 和 Chrome。
</p>
<blockquote>
在 XHTML 文檔中,要把 async 屬性設置為 async="async"。
</blockquote>
<h2>
2.1.4 在XHTML中的用法①
</h2>
<p>
可擴展超文本標記語言,即 XHTML(Extensible HyperText Markup Language),是將 HTML 作為XML 的應用而重新定義的一個標準。編寫 XHTML 代碼的規則要比編寫 HTML 嚴格得多,而且直接影響能否在嵌入 JavaScript 代碼時使用<script/>標簽。以下面的代碼塊為例,雖然它們在 HTML 中是有效的,但在 XHTML 中則是無效的。
</p>
<pre><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></pre>
<p>
在 HTML 中,有特殊的規則用以確定<script>元素中的哪些內容可以被解析,但這些特殊的規則在 XHTML 中不適用。這里比較語句 a < b 中的小于號(<)在 XHTML 中將被當作開始一個新標簽來解析。但是作為標簽來講,小于號后面不能跟空格,因此就會導致語法錯誤。
</p>
<blockquote>
① HTML5 正快速地被前端開發人員采用,建議讀者在學習和開發中遵循 HTML5 標準,本節內容可以跳過。
</blockquote>
<p>
避免在 XHTML 中出現類似語法錯誤的方法有兩個。一是用相應的 HTML 實體<code>(&lt;)</code>替換代碼中所有的小于號(<code><</code>),替換后的代碼類似如下所示:
</p>
<pre><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></pre>
<p>
雖然這樣可以讓代碼在 XHTML 中正常運行,但卻導致代碼不好理解了。為此,我們可以考慮采用另一個方法。
</p>
<p>
保證讓相同代碼在 XHTML 中正常運行的第二個方法,就是用一個 CData 片段來包含 JavaScript 代碼。在 XHTML(XML)中, CData 片段是文檔中的一個特殊區域,這個區域中可以包含不需要解析的任意格式的文本內容。因此,在 CData 片段中就可以使用任意字符——小于號當然也沒有問題,而且不會導致語法錯誤。引入 CData 片段后的 JavaScript 代碼塊如下所示:
</p>
<pre><script type="text/javascript">
< ![CDATA[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></pre>
<p>
在兼容 XHTML 的瀏覽器中,這個方法可以解決問題。但實際上,還有不少瀏覽器不兼容 XHTML,因而不支持 CData 片段。怎么辦呢?再使用 JavaScript 注釋將 CData 標記注釋掉就可以了:
</p>
<pre><script type="text/javascript">
//<![CDATA[
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></pre>
<p>
這種格式在所有現代瀏覽器中都可以正常使用。雖然有幾分 hack 的味道,但它能通過 XHTML 驗證,而且對 XHTML 之前的瀏覽器也會平穩退化。
</p>
<blockquote>
在將頁面的 MIME 類型指定為"application/xhtml+xml"的情況下會觸發XHTML 模式。并不是所有瀏覽器都支持以這種方式提供 XHTML 文檔。
</blockquote>
<h2>
2.1.5 不推薦使用的語法
</h2>
<p>
在最早引入<script>元素的時候,該元素與傳統 HTML 的解析規則是有沖突的。由于要對這個元素應用特殊的解析規則,因此在那些不支持 JavaScript 的瀏覽器(最典型的是 Mosaic)中就會導致問題。具體來說,不支持 JavaScript 的瀏覽器會把<script>元素的內容直接輸出到頁面中,因而會破壞頁面的布局和外觀。
</p>
<p>
Netscape 與 Mosaic 協商并提出了一個解決方案,讓不支持<script>元素的瀏覽器能夠隱藏嵌入的JavaScript 代碼。這個方案就是把 JavaScript 代碼包含在一個 HTML 注釋中,像下面這樣:
</p>
<pre><script>
< !--
function sayHi() {
alert("Hi!");
}
//-->
</script></pre>
<p>
給腳本加上 HTML 注釋后, Mosaic 等瀏覽器就會忽略<script>標簽中的內容;而那些支持JavaScript 的瀏覽器在遇到這種情況時,則必須進一步確認其中是否包含需要解析的 JavaScript 代碼。
</p>
<p>
雖然這種注釋 JavaScript 代碼的格式得到了所有瀏覽器的認可,也能被正確解釋,但由于所有瀏覽器都已經支持 JavaScript,因此也就沒有必要再使用這種格式了。在 XHTML 模式下,因為腳本包含在XML 注釋中,所以腳本會被忽略。
</p>