Html 的空白符處理規則
Html 中的“空白符”包括空格 (space)、制表符 (tab)、換行符 (CR/LF) 三種。
我們知道,在默認情況下,Html 源碼中的空白符均被顯示為空格,並且連續的多個空白符會被視為一個,或者說,連續的多個空白符會被合並。
然而在有些時候,我們希望 Html 源碼中的多個連續空格在網頁浏覽器中可以真實地呈現,或者需要源碼中的換行符能起到真正的換行作用。於是,我們發現了 <pre> 標簽,它可以真實還原它內部文本的空白符的真實情況。
於是我們經常會把一段表示計算機代碼的文本放進 <pre> 標簽中,它們在浏覽器中會表現出自身的空格縮進和換行效果,而不需要我們增加額外的樣式和標簽來控制它的縮進和換行。
隨著對 CSS 的了解不斷深入,我們發現,原來這一切都是 white-space 屬性在安排。<pre> 元素之所以如此神奇,是因為它自身具有 {white-space: pre;} 這一默認樣式。
white-space 屬性
CSS 中的 white-space 屬性用於設置文本空白符的處理規則,這其中包括:是否合並空白符、是否保留換行符、是否允許自動換行。各屬性值的不同行為如下表所示:
white-space 屬性值一覽表
normal
合並
忽略
允許
CSS 1
nowrap
合並
忽略
不允許
CSS 1
pre
保留
保留
不允許
CSS 1
pre-wrap
保留
保留
允許
CSS 2.1
pre-line
合並
保留
允許
CSS 2.1
(注:在 CSS1/2 下,white-space 屬性只可應用於塊級元素;在 CSS 2.1 下,可應用於所有元素。)
如果我們需要某個容器元素具有類似 <pre> 元素的空白符處理行為,則為它設置 {white-space: pre;} 樣式即可。
對 pre-wrap 的需求
我們先解釋一下上述表格中的“自動換行”行為,它是指某元素內部的文本流按照文本方向排版,當文本流遇到限制其繼續延伸的邊界時,是否換行。“不允許自動換行”則意味著文本流會溢出該元素。
因此,{white-space: pre;} 樣式有時候並不能滿足我們的期望。比如,在某些不需要特別嚴謹的場合,或者排版某些對換行不敏感的代碼片斷(比如 Html 或 CSS)的時候,我們不希望代碼片段中的一行長代碼令它的容器元素產生水平滾動條,因為那樣不便閱讀。我們希望在這種情況下,長代碼自動換行就好。
這時,對照一下上表中各屬性值的不同行為特征,我們會發現 pre-wrap 這個屬性值脫穎而出——它正是我們所需要的。
對 pre-wrap 的另一種需求
再來看另一種實戰中可能會遇到的情形。
表單中的文本域(<textarea> 元素)可以接受包含換行符的文本數據,這是它有別於文本框(text 類型的 <input> 元素)的重要特征之一,所以我們通常也稱它為“多行文本框”。
隨之而來的一個問題就是,我們通過多行文本框提交多行文本數據,是為了在網頁上最終顯示出多行文本。但由於浏覽器對 HTML 源代碼默認進行空白符合並處理,為了確保我們提交的多行文本數據最終在網頁上正確地呈現出多行的形態,通常需要在服務器端做處理,比如將文本中的換行符轉換為 Html 的換行標簽 <br>,再寫入數據庫;或者從數據庫中讀出文本數據時進行類似的轉換操作。
這樣當服務器向網頁輸出這些文本數據時,原始的回車狀態才能得到再現。
但是,由於設計失誤(或系統有意限制),服務器端可能就不會做這樣的處理。從而導致這些文本信息中的換行符無法呈現出換行效果,取而代之的是一個小空格。
(下圖為 cnBeta 網站對評論文本的兩種不同處理方式:左側為普通評論,可能為了限制各條評論的高度、防止惡意刷屏,系統未做換行符轉換處理;右側為熱門評論,系統進行了處理。)
如果服務器端因為疏忽沒有做換行符轉換處理,那麼在前端是否可以用最小的代價來補救?這時,pre-wrap 就可以發揮作用——無需做任何的額外處理,直接為文本的容器元素設置 {white-space: pre-wrap;} 樣式,就可以還原多行文本的真實狀態。
杯具的 IE6 和 IE7
再來看一下上面的表格,我們發現 pre-wrap 是從 CSS 2.1 才開始引入的屬性值。然而,目前網民使用最為廣泛的 IE6 和 IE7 浏覽器都是基於 CSS1 和部分 CSS2 的,它們完全不能識別 pre-wrap,當然也無法實現 pre-wrap 的空白符處理行為。
在瘋狂地問候了微軟、IE 及其相關人等之後,網頁開發者們還是不得不面對這個問題——如何在 IE6,7 下實現 pre-wrap 的效果?
在 IE6,7 下變通實現 pre-wrap
經常反復測試,我們找到了在 IE6,7 下變通實現 pre-wrap 效果的方法。
比如,有如下 Html 結構:
<div class="content">這是一段多行文本數據其中某些文本行會非常長從而溢出容器比如你現在看到的這行行與行之間有換行符但沒有使用 Html 換行標簽</div>
我們需要將 .content 元素設置為 pre-wrap 樣式,理想情況下只需要編寫如下 CSS 代碼就可以了。、
.content {
white-space: pre-wrap;
}
但為了應付 IE6,7,我們需要將上述 CSS 代碼修改如下:
.content {
white-space: pre-wrap;
*white-space: pre;
*word-wrap: break-Word;
}
這樣就可以了,我們在各浏覽器中實測一下,可以發現我們需要的效果完美實現。
當然,你可能注意到了,我們使用了一點兒 CSS hack。別擔心,它們條理清晰並且容易維護,我覺得這可以接受。在面對低能浏覽器的時候,我們只能給予它們一些額外關照。
原理
如果你是一個實用主義者,那麼文章到這裡已經結束了。你可以把代碼存下然後走人,或者繼續浏覽 CSS魔法 的其它文章。如果你是一個充滿好奇心的 CSS 學習者,那麼我很樂意與你一起來分析一下它的實現原理。
在上面的最終版 CSS 代碼中,很顯然對於標准浏覽器,我們是用正常的 {white-space: pre-wrap;} 來實現所需效果的。而對於 IE6,7,我們使用了 CSS hack,讓它接受額外的樣式聲明,使用其它方法來實現類似 pre-wrap 的效果。
首先,在 IE6,7 下,{white-space: pre-wrap;} 這條樣式聲明由於不能識別而被丟棄,於是我們為 .content 另外設置了 {white-space: pre;} 的樣式。我們已經很熟悉 pre 了,它的特性與我們想要的 pre-wrap 效果只有一點區別,即 pre 不允許自動換行,也就是說,較長的文本行可能會溢出其容器元素。
因此,接下來,為了讓這些較長的文本行自動換行,我們為 .content 元素設置 {word-wrap: break-Word;} 樣式(謹慎起見,我們用 CSS hack 將這條聲明隔離給 IE6,7;不過即使將它暴露給所有浏覽器,它也是無害的)。這條聲明負責對 .content 元素內的文本行進行約束,並強制其換行。也就是說,{white-space: pre;} 完成了識別文本換行符的任務,剩下的自動換行的任務交由 {word-wrap: break-Word;} 來完成。
回到前面的原理分析,其實我們會發現一個矛盾,{white-space: pre;} 很倔犟地不願換行,而 {word-wrap: break-Word;} 則要求內部文本自動換行。面對這樣的沖突,浏覽器如何決斷?
在 CSS 中,控制文本換行方式的屬性有很多,當發生沖突的時候,某些屬性在文本排版中的優先級更高,因而會在沖突中勝出,決定最終的文本樣式。很顯然,在上面的這起沖突中,{word-wrap: break-Word;} 更加強勢,倔犟的文本行最終還是乖乖地換行了。
結語
感謝你看到了這裡,希望這篇文章對你有所幫助!
插播 Word-wrap 的相關資料
CSS 發展至今經歷了多個版本,但它對文本排版的控制仍然不夠精確和靈活。於是微軟的 IE 浏覽器開發了一些私有屬性,擴展了 CSS 的文本排版功能,尤其可貴的是,這些擴展屬性大多考慮到了非拉丁語系語言的排版規則。由於這些私有擴展屬性確實很有價值,它們被整理並收錄到了 CSS3 草案中。
Word-wrap 屬性就是其中很有代表性的例子。它決定了文本行超過容器的邊界時是否斷開轉行。目前這一屬性已經得到了絕大多數主流浏覽器的支持。