你在寫 HTML5 程式碼的時候,是否糾結過應該寫 <br />
還是 <br>
,是寫 <input />
還是寫 <input>
。寫 <script src="script.js" />
為什麼是錯的呢?反正我糾結過,而且我發現這個話題其實比我想象中有意思的多。
如果你對我的研究過程不感興趣,你可以直接跳到“合法性”這一節得到答案。
無內容元素(Void elements)
無內容元素是一種不能包含任何內容的特殊元素。而其他元素,比如 <div>
,則既可以不包含任何內容,又可以包含另一個元素或者文字。
比較常見的無內容元素有:
<br>
<hr>
<img>
<input>
<link>
<meta>
不太常見的無內容元素有:
<area>
<base>
<col>
<command>
<embed>
<keygen>
<param>
<source>
<track>
<wbr>
現存所有的無內容元素就是以上這些了。
<br></br>
這樣的寫法是不合法的 HTML 寫法,因為它暗示 br
可以包含內容(但 <br>你好!</br>
是完全沒有意義的)。而 <br>
和 <br />
這兩種寫法都很常見。
儘管我們都知道 XHTML 強制你必須寫成 <br />
,但 HTML 裡卻沒有明文規定。
追溯歷史
為了完全地瞭解無內容元素,我們有必要了解一下它的歷史。
HTML、XML 和 XHTML 都是基於 SGML 的,SGML 的全稱是“標準通用標記語言”,起草於1986年。
HTML 和 XML 都派生自 SGML,其中 XML 是 SGML 的約束性子集,而 XHTML 是基於 XML 的。
XHTML 基本上和 HTML 一樣,但是是基於 XML 的。
知道這層關係後,接下來進入本文最有意思的部分。
SGML 有一個特性叫做“無尾標籤(NET,Null End Tag)”。當標籤內只有簡單的文字的時候,使用無尾標籤就可以無須再閉合這個標籤了。比如你可以把 <quote>Quoted text</quote>
寫成 <quote/Quoted text/
。(你沒有看錯,這個標籤中不含 >
。)
那麼,不包含任何內容的標籤就可以寫成 <quote//
了,其中 quote 是標籤名,第一個 /
用於啟用無尾標籤,第二個 /
表示無尾標籤結束。
如果按照這個邏輯,<br//
中的前半部分 <br/
會被解析為 <br>
,那麼 <br/>
豈不是要被解析為 <br>>
了嗎?如果你和我想得一樣,你肯定也會覺得這種語法很蠢。
不幸的是 HTML4 規範的制定者們不這麼認為,並且把它寫進規範裡了。不過顯然當時的瀏覽器廠商對這種語法也不以為然,支援的程度不大。(在這一點上,說不定瀏覽器廠商們做了一件好事。)
XML (也適用於 XHTML)規範的制定者意識到這種語法不怎麼好,就直接沒有包含無尾標籤這種特性,
同時為無內容標籤提供了一種比較好理解的語法。這種語法的名字叫做“無元素標籤”,它看起來是這樣的:<br />
。這種語法看起來非常自然,因此當時的大多數開發者都認為這才是正確的寫法。
幸運地是 HTML 一直在改進,W3C 的成員一直在從他們過去作出的錯誤中學習經驗教訓。因此 HTML5 相比之前的版本才有這麼大的進步。
在介紹 HTML5 的新語法時, W3C 說:
HTML5 的語法完全相容 HTML4 和 XHTML1,但是不相容 SGML 中那些晦澀的 HTML4 特性。比如無尾標籤(
<em/content/
)。
HTML5 好樣的!
(我覺得他們應該保留“短標籤”特性,比如 <strong>不錯喲</>
,我覺得這個特性很酷。不過,至少現在的 HTML 已經不再是那麼雜亂無章了。)
合法性
好吧,我們回到文章開頭關於合法性的問題,目前的 HTML5 規範中關於非內容標籤的解釋是這樣的:
此類標籤應由下列部分組成,順序須與下表保持一致:
- 一個 “<” 字元。
- 標籤名。
- 此項可選,一個或多個屬性,每一個屬性的前面必須有一個或多個空格。
- 此項可選,一個或多個空格。
- 此項可選,一個 “/” 字元,此項只能在無內容元素中出現。
- 一個 “>” 字元。
倒數第二部分的 “/” 字元是可選的,而且沒有任何實際含義。所以 <br>
和 <br />
其實沒有實質區別。
正確性
喜歡 XML 和 XHTML 的開發者可能會說,“對呀,雖然 /
是可選的,但是 <br />
的寫法‘更正確’一些。”
我必須告訴你你錯了。事實上,有觀點認為無內容標籤裡的 /
其實是一個被容忍的語法錯誤。這種容忍是基於相容性考慮的,它使得所有瀏覽器和解析器都把 <br>
和 <br />
同等對待。
關於這一點,Google 程式碼風格指南 也明確規定了不要關閉無內容標籤。
缺點
當然,不關閉無內容標籤也有弊端,不過我認為這掩蓋不了它的優點:使你的程式碼乾淨簡潔。
第一個缺點就是開發者必須知道哪些標籤的無內容標籤。假設你不知道 <img>
是不是無內容標籤,那麼當你找不到它的閉合標籤時,你就會疑惑到底應不應該關閉這個標籤。不過無內容標籤總共也只有那麼幾個,而且一般一眼就能看出來某個標籤是不是無內容標籤。
第二個缺點是編輯器可能對沒有閉合的無內容標籤處理不好。編輯器的開發者們必須瞭解無內容標籤,提供恰當的語法高亮和程式碼補全。當你在編輯器裡寫了一個 <input>
,編輯器必須要知道它後面永遠不會接一個 </input>
。
但是這些功能實現起來很簡單,我所知道的編輯器對這方面支援得都還挺好,所以這算不上一個真正的缺點。
我對無內容標籤的看法
我覺得無內容標籤這個概念其實是可以從 HTML 中剔除的,我們完全可以給這些標籤新增內容,來代替它的某些屬性。
以 <img>
標籤為例,它有一個強制的 alt
屬性,這個屬性的存在是為了讓那些看不到圖片的使用者(可能是因為生理缺陷,也可能是因為他們使用的裝置不支援圖片)知道這個圖片的內容是什麼(如果圖片只是處於美觀考慮,你其實不應該新增 alt
屬性)。
我的問題來了:為什麼不用 <img>
的內容代替 alt
屬性?我認為這樣寫更直觀:
<img src="doge.png">Image of doge</img>
。
<meta>
標籤甚至還有一個叫 content
的屬性!為什麼不直接把 content
的值寫在標籤的內容裡呢?<input value="Value content">
應該寫成 <input> Value content</input>
,就像 <textarea>
那樣。其他標籤不一而足。
所以真正應該保留的無內容標籤只有少數幾個,只不過 W3C 必須考慮向後相容性,所以要改變現狀還是很困難的。
最後的想法:<script>
標籤
這個標籤真的很困擾我,因為它的含義很簡單,寫法卻很羅嗦。<script src="my-script.js"></script>
這種寫法看起來似乎是錯的,因為 <script>
的內容與 my-script.js
並沒有邏輯關聯。(HTML 規範允許你同時給它新增內容和 src
屬性)
問題在於 <script>
標籤不是一個無內容標籤,你可以把 JavaScript 寫在它裡面。所以這裡並沒有可選的 /
閉合標記(譯註:這就是為什麼<script src="script.js" />
的寫法是錯的)。
使用 <link>
標籤來代替 <script>
就完美了,因為它已經被用於匯入外部檔案,而且提供了所有必需的屬性。當然,Web 平臺總是需要考慮向後相容,不然所有不支援這種語法的舊式瀏覽器全都無法解析你的頁面了。