需要用CSS換行的時機通常是在使用definition lists的時候,但有時在其他的場合也會有同樣需求。會使用definition lists因為我們想做一個良好的網路公民,所以使用適當、有語意的標記語言,即使在視覺上需要的其實是項目名稱和內容的配對組合。例如下面這段標記語言結構:
<dl>
<dt>Name:</dt>
<dd>Joaquin Phoenix</dd>
<dt>Born:</dt>
<dd>October 28, 1974</dd>
<dt>Filmography:</dt>
<dd>Her</dd>
</dl>
(原生的definition lists樣式)
通常我們會想要的是項目名稱在左,項目內容在右,新的項目在下一行。所以我們可能會加入以下的CSS:
dd {
margin: 0;
font-weight: bold;
}
然而因為<dt>
和<dd>
都是block元素,上面的樣式只是減少了縮排,清單裡的每個項目仍然單獨在一行。
接著我們可能會試試看變更<dt>
和<dd>
的display
屬性,然後搞得一團亂。(你來試試看)
(設成inline全部都連在一起了)
除了抱怨CSS不夠力,在一切絕望只剩下調整HTML結構這個大絕之前,有沒有方法能讓我們同時保持理性和有條理的HTML結構呢?
基本上,我們需要在每一個<dd>
的後面加上換行標記,如果不在意HTML語意的話,用<br>
就行。
<dl>
<dt>Name:</dt>
<dd>Joaquin Phoenix<br /></dd>
</dl>
然後為<dt>
和<dd>
加上display: inline
就OK了。當然,這不只在維護上是不好的做法,同時也讓我們的結構變大了。如果可以用自動產生的內容來加上換行標記,我們就解決問題了!有這樣的東西嗎?
有一個Unicode符號和換行標記的0x000A
相同,在CSS裡寫做\000A
,或是簡寫成\A
。如果我們在<dd>
的偽元素::after
裡加入這個符號是不是就能自動換行了?
dd::after {
content: "\A";
}
結果卻是一點反應也沒有,是哪裡出了問題?原因出在偽元素是放在<dd>
裡面,在HTML裡,所有換行都是被忽略的,所以我們的方法也被視為是一般的換行而被無視掉。要保留空白和換行的話,會使用white-space: pre;
。我們也可以這麼做。
dt, dd { display: inline; }
dd {
margin: 0;
font-weight: bold;
}
dd::after {
content: "\A";
white-space: pre;
}
這下子得到我們要的效果了,不過它夠彈性嗎?在演員參與過的作品再加入一部電影會發生什麼事?
<dl>
<dt>Filmography:</dt>
<dd>Her</dd>
<dd>Joker</dd>
</dl>
新加入的電影換到新的一行去了,因為每一個<dd>
都加入了換行符號的關係。如果同一個項目的內容都能在一行,以逗點隔開就好了。
所以我們應該只要找出任何在<dt>
前面一個的<dd>
,換行符號加在這些<dd>
上就可以了。可是用過CSS的人都知道,CSS的選擇器只能選取後面的元素,不能向前選,我們要找另一個方法。其中之一是與其把換行符號加在dd::after
,試試看加在dt::before
。
dt::before {
content: "\A";
white-space: pre;
}
不過這樣子就讓第一個項目前面多了空行,讓我們再試試更多的選擇方法:
dt:not(:first-child)
dt ~ dt
dd + dt
我們決定用最後一種,因為如果一個值有多個<dt>
時它仍然可以用。
最後要在不同的內容之間加上逗號和空格,直覺上會希望「當<dd>
後面也有<dd>
時,為前面那個加上逗號」,不過CSS是不能選擇之前的元素,我們只能改變方法,在二個連續的<dd>
情況下,給後面那個的::before
偽元素加入逗號。
dd + dt::before {
content: "\A";
white-space: pre;
}
dd + dd::before {
content: ", ";
font-weight: normal;
}