源起
我們經(jīng)常需要“修復(fù)”一個(gè)老生常談的“bug”,那就是文本的自動(dòng)換行問題。在專業(yè)術(shù)語上,這種期望得到的渲染現(xiàn)象被稱作“word wrap”,即文本處理器有能力把超出頁邊的整個(gè)詞自動(dòng)傳到下一行。
在現(xiàn)實(shí)項(xiàng)目中,尤其是在測試階段,鑒于測試使用非常極端的測試用例,我們經(jīng)常需要“修復(fù)”如圖所示的這個(gè)問題:
長單詞溢出
圖中,極長的這個(gè)英文單詞(雖然是生造的)為了保證完整的顯示,無奈地超出了容器的限制,它溢出了。為了“修復(fù)”這個(gè)“問題”,使得無論東亞還是西歐文字都能被限定在容器的尺寸范圍內(nèi),我們一般會(huì)加上諸如“word-wrap: break-word; word-break:break-all;”這樣的屬性,令我們滿意(好吧,其實(shí)是令測試滿意)的結(jié)果如圖所示:
長單詞被強(qiáng)行斷行
從以結(jié)果現(xiàn)象為導(dǎo)向的觀點(diǎn)出發(fā),這個(gè)“bug”被“修復(fù)”了,但是在做了三五次這樣的重復(fù)工作后,我開始產(chǎn)生這樣幾個(gè)疑問:
word-wrap 和 word-break 究竟是什么?
為什么會(huì)樂此不疲地重復(fù)碰到這個(gè)問題?
這個(gè)問題是問題么?
規(guī)則
在解惑之前,有幾個(gè)關(guān)乎問題本質(zhì)的客觀現(xiàn)實(shí)需要指出,因?yàn)檫@些“常識(shí)”最容易被人忽視:
CJK 文字和 !CJK 文字有各自的排版規(guī)則。
在這里,CJK 代表 Chinese, Japanese, and Korean,即東亞文字,!CJK 就是非東亞文字,大多數(shù)情況下是西歐文字。
在文字的呈現(xiàn)規(guī)則上,兩者很不相同,CJK 文字中,一個(gè)字母就是一個(gè)字素(單詞),獨(dú)立成義,!CJK 文字中,一些字母組成一個(gè)字素,并且字素們由連接符“-”連接,或由空格“ ”分隔。
有關(guān) CJK 文字更多的排版規(guī)則上,比較有代表性的是:對(duì)中文來說,標(biāo)點(diǎn)符號(hào)不能成為行首(特殊除外);雙字長的標(biāo)點(diǎn)符號(hào)(省略號(hào)、破折號(hào))不能斷開。
對(duì)于 !CJK,主要是:單詞不能在中間不合法地?cái)嚅_(合法情況例如從連接符處斷開);標(biāo)點(diǎn)符號(hào)不能成為行首(特殊除外)
解惑一
word-wrap 和 word-break 究竟是什么?對(duì)于這個(gè)問題,直接拜訪 W3C 官方,找到 CSS3 草案:
http://www.w3.org/TR/2010/WD-css3-text-20101005/,再訪問微軟,借鑒諸如
http://msdn.microsoft.com/en-us/library/ms531184%28VS.85%29.aspx得出的結(jié)論如下:
word-wrap, line-break, word-break 這幾個(gè)屬性都是 MS 的獨(dú)立實(shí)現(xiàn),隨后其他瀏覽器也不同程度地實(shí)現(xiàn)了其中的某些,之后,這幾個(gè)屬性都被吸納為 CSS3 標(biāo)準(zhǔn)。在對(duì)文字排版的渲染上,微軟還是走在前面的。
在現(xiàn)有的 CSS3 草案中,關(guān)乎到文字排版的幾個(gè)重要屬性有:white-space, text-wrap, word-wrap, line-break, word-break
根據(jù) CSS3 的描述,列出這些屬性各自的要點(diǎn),這部分讀者可以跳過……
white-space 是 white-space-collapsing 和 text-wrap 的縮寫
屬性 設(shè)置 white-space-collapsing 設(shè)置 text-wrap 空行 空格 文字自動(dòng)換行 效果
normal collapse normal collapse collapse wrap 忽略多余空行和空格,文字自動(dòng)換行
pre preserve none preserve preserve no wrap 保留所有空行和空白,文字不自動(dòng)換行
nowrap collapse none collapse collapse no wrap 忽略多余空行和空格,文字不自動(dòng)換行
pre-wrap preserve normal preserve preserve wrap 保留所有空行和空白,文字自動(dòng)換行
pre-line preserve-breaks normal preserve collapse wrap 合并多余空格,保留多余空行,文字自動(dòng)換行
text-wrap 定義文本的自動(dòng)換行效果
屬性 效果
normal 在允許的斷點(diǎn)處自動(dòng)換行
none 文本不會(huì)自動(dòng)換行;對(duì)于不“合身”的容器,文本將會(huì)溢出
unrestricted 在任意的文法單詞間都可斷行,比 normal 的限制要松散很多
suppress 除非斷行處沒有其他任何允許的斷點(diǎn),方可進(jìn)行斷行,這比 unrestricted 嚴(yán)格,比 normal 松散
word-wrap 執(zhí)行最激進(jìn)的單詞斷行控制,從單詞的內(nèi)部斷開以防止文本溢出容器并且完全適應(yīng)容器的寬度
在 IE 的實(shí)際效果中,word-break 的效果要激進(jìn)得多,它窮兇極惡地?cái)嚅_所有單詞(如果到達(dá)邊界的話)
屬性 效果
normal 僅在允許的文本斷點(diǎn)處自動(dòng)換行
break-word 如果一行中沒有其他可接受的斷點(diǎn),那么將強(qiáng)行斷開文本單詞
line-break 是斷行的規(guī)則,針對(duì)東亞文字
基本是針對(duì)日文的換行規(guī)則
word-break 是斷行的規(guī)則,針對(duì)非東亞文字
屬性 效果
normal 根據(jù)特定非東亞文字自己的規(guī)則來決定是否自動(dòng)斷行
break-all 允許非東亞語言文本行的任意字內(nèi)斷開。該值適合包含一些非東亞文本的東亞文本
keep-all 不允許非東亞語言文本行的任意字內(nèi)斷開。該值適合包含一些東亞文本的非東亞文本
hyphenation 文本會(huì)在合適的連字符處斷開,這需要瀏覽器的支持
做一個(gè)歸納:專門用于控制文本自動(dòng)換行功能的屬性是 text-wrap 和 word-wrap,而 line-break 和word-break 用來控制斷行和單詞邊界分隔,根據(jù) W3C 的描述來說,word-wrap是最激進(jìn)的自動(dòng)換行方式,可以強(qiáng)行斷開單詞。而現(xiàn)實(shí)情況是,word-break: break-all; 的方式要更為激進(jìn),如圖:
word-wrap
word-break
對(duì)比 word-wrap: break-word; 和 word-break: break-all;,兩者都將文本限定在了容器的范圍內(nèi),只是 break-all 將所有單詞,不論長短地,通通截?cái)啵琤reak-word 則非如此,它盡量地遵從了排版規(guī)則。
兼容性
由于幾個(gè)屬性都來自于微軟(部分來自于 CSS3),那么理所當(dāng)然 IE 是支持最良好的,不過對(duì)于浮動(dòng)元素,IE67 的表現(xiàn)會(huì)有些 bug(可在文后給出的 demo 中驗(yàn)證)。
至于其他瀏覽器,F(xiàn)F 3.6 不支持 word-break;Chrome 7 支持良好;Safari 5 同 Chrome;Opera 10 同 FF
解惑二三
碰到相關(guān)問題的場景大體是兩個(gè):
測試使用了很極端的測試用例(比如 asdfasdfasdfasdfasdfasdfasdf)
IE67 下,在寬度不大的容器中使用了浮動(dòng)元素,同時(shí)浮動(dòng)元素內(nèi)包含了長的串,如圖:
IE67 中浮動(dòng)盒子杯具
對(duì)于場景一,使用 word-wrap: break-word;
對(duì)于場景二,使用 IE67 的 hack,word-break: keep-all; 或者用 inline-block 來代替浮動(dòng)(IE67 中,hasLayout 的 inline 盒子大體等同于 inline-block)
回頭看疑問二,我們?yōu)槭裁磿?huì)樂此不疲地重復(fù)碰到這個(gè)問題?原則上,各個(gè)瀏覽器默認(rèn)的文字排版方式已經(jīng)很好地顧及了 CJK 文字和 !CJK文字,根據(jù)各個(gè)語言自己的規(guī)則來呈現(xiàn)排版,不應(yīng)該出現(xiàn)詭異的問題。所以,對(duì)于上面的兩個(gè)問題場景,之所以產(chǎn)生場景一,是因?yàn)槭褂昧藰O端的測試用例,但是在現(xiàn)實(shí)中,這種極長的英文單詞是根本不存在的(特殊行業(yè)除外),又,即使英文單詞較長,也不應(yīng)該突兀地截?cái)?,這有違西歐文字的排版規(guī)則。所以我認(rèn)為,如果在現(xiàn)實(shí)環(huán)境下發(fā)生場景一中的問題,責(zé)任應(yīng)該在于版面的設(shè)計(jì),比如容器寬度太小,而不是去截?cái)辔谋?;?duì)于場景二,應(yīng)該歸咎于 IE67 的渲染bug,這時(shí),使用 inline-block 代替,或用 word-break: keep-all; 來給犯錯(cuò)的瀏覽器擦屁股。
實(shí)踐方案
對(duì)于我們輸出的內(nèi)容(可控的),不使用任何 word-wrap 和 word-break 等屬性,對(duì)于可能產(chǎn)生的長單詞溢出這種小概率事件,首先考慮容器寬度是否合理,其次可以為長單詞添加連字符“-”以便合理地?cái)嚅_,最后設(shè)置 overflow: hidden; 避免視覺上的溢出。
對(duì)于用戶輸出的內(nèi)容(不可控的),比如評(píng)論等,由于不排除用戶會(huì)輸入“dddddddddddd”這樣沒營養(yǎng)的垃圾數(shù)據(jù),使用 word-wrap: break-word; 來強(qiáng)行截?cái)唷?div style="height:15px;">