用 CSS 分片打破盒子
已發表: 2022-03-10在本文中,我將向您介紹 CSS Fragmentation 規範。 您可能從未聽說過它,但是,如果您曾經創建過打印樣式表並想要控制頁面之間的內容中斷位置,或者多列佈局並想要阻止列之間的圖形中斷,那麼您已經遇到過它。
我發現人們經常用 multicol 報告的問題實際上是瀏覽器支持碎片的問題。 在快速了解本規範中包含的屬性之後,我將解釋瀏覽器支持的當前狀態以及您可以做的一些事情,以使其在您的 multicol 和打印項目中正常工作。
什麼是碎片化?
CSS 中的碎片化描述了將內容分解為不同框的過程。 目前,我們在網絡上有兩個地方可能會遇到碎片:當我們打印文檔時,以及如果我們使用多列佈局。 這兩件事本質上是一樣的。 當您打印(或保存為 PDF)網頁時,內容會被分割成打印內容所需的頁數。
當您使用 multicol 時,內容被分割成列。 每個列框就像分頁上下文中的一個頁面。 如果您認為一組列很像一組頁面,那麼它可能是一種有用的方式來思考 multicol 以及碎片如何在其中工作。
如果你看一下 CSS Fragmentation Specification,你會看到提到的第三個碎片上下文——那個上下文是 Regions。 由於當前沒有可用的區域實現,因此我們不會在本文中討論它,而是查看您在工作中可能遇到的兩個上下文。
塊和內聯框
我將在本文中大量提及塊框。 頁面的每個元素都有一個框。 其中一些框被佈置成塊:段落、列表項、標題。 據說這些參與了塊格式化上下文。 其他是內聯的,例如段落中的單詞、跨度和錨元素。 這些參與內聯格式化上下文。 簡而言之,當我提到塊框時,我指的是圍繞段落之類的框。 在處理碎片時,重要的是要知道您正在處理哪種盒子。
有關塊和內聯佈局的更多信息,請參閱 MDN 文章“正常流程中的塊和內聯佈局”。 這是我們可能在某種程度上都理解但以前可能沒有遇到過的術語的事情之一。
控制休息
無論您是創建打印樣式表、使用特定的打印用戶代理製作 PDF 還是使用 multicol,您有時都會遇到類似這樣的問題。
在下面的多列示例中,我有一些內容顯示為三列。 內容的中間是一個框出的區域,它被分成兩列。 我不希望這種行為——我希望盒子保持在一起。

為了解決這個問題,我將屬性break-inside: avoid
添加到框中。 當元素處於碎片上下文中時, break-inside
屬性控件會在元素內部中斷。 在支持此屬性的瀏覽器中,該框現在將保留在其中一列中。 列看起來不太平衡,但是,這通常比最終將 boxout 拆分為列更好。
請參閱 (Rachel Andrew.
break-inside
屬性是碎片規範中詳述的屬性之一。 完整的屬性列表如下:
-
break-before
-
break-after
-
break-inside
-
orphans
-
widows
-
box-decoration-break
在我們進入瀏覽器中實際發生的事情之前,讓我們看看這些應該如何工作。
break-before
和break-after
屬性
有兩個屬性可以控制塊級框之間的中斷: break-before
和break-after
。 如果您有一個h2
後跟兩個段落<p>
您有三個塊框,您將使用這些屬性來控制標題和第一段之間或兩個段落之間的分隔符。
這些屬性用於選擇器,這些選擇器針對要在之前或之後中斷的元素。
例如,您可能希望每次出現第 2 級標題時,您的打印樣式表都會跳到一個新頁面。 在這種情況下,您將在h2
元素上使用break-before: page
。 這控制了碎片並確保在h2
元素的框之前總是有一個中斷。
h2 { break-before: page; }
另一個常見的要求是防止標題成為頁面或列上的最後一件事。 在這種情況下,您可以使用值為avoid
break-after
。 這應該防止在元素框之後直接中斷:
h1, h2, h3, h4 { break-after: avoid; }
片段中的片段
您可能有一個元素碎片嵌套在另一個元素中。 例如,在被分頁的東西里面有一個多列。 在這種情況下,您可能希望控制頁面而不是列的中斷,或者相反。 這就是為什麼我們有諸如page
之類的值,它總是在元素之前或之後強制中斷,但僅當片段是頁面時。 或者avoid-page
,這將避免元素之前或之後的中斷,僅用於分頁上下文。
這同樣適用於列。 如果您使用 value column
,這將始終在該元素之前或之後強制中斷,但僅適用於多列上下文。 值avoid-column
將防止在多列上下文中中斷。
第 4 級規範中有一個always
值,表示您想要突破所有內容——頁面或列。 但是,作為規範的最新補充,它目前對我們沒有用處。
分頁媒體的附加值
如果您正在創建一本書或雜誌,則您有左頁和右頁。 您可能想要控制中斷,以便將某些內容強製到跨頁的左側或右側頁面上。 因此,使用以下命令會在h2
之前插入一到兩頁分頁符,以確保它被格式化為正確的頁面。
h2 { break-before: right; }
還有與頁面進展相關的正向和反向值,因為以垂直或從右到左語言編寫的書籍與用英語編寫的書籍具有不同的頁面進展。 我不會在本文中進一步討論這些值,因為我主要關心的是這次瀏覽器可能提供的功能。
break-inside
我們已經看到了break-inside
屬性的一個例子。 此屬性控制塊框內的中斷,例如在段落、標題或 div內。
您可能不想破壞的內容可能包括如上所述的框出:您不希望標題與圖像、表格、列表等分離的圖形。 添加break-inside: avoid
在任何碎片上下文中您不希望破壞的任何容器。 如果您只想避免列之間的中斷,請使用break-inside: avoid-column
和頁之間break-inside: avoid-page
。
orphans
widows
屬性
orphans
和widows
屬性處理在中斷之前或之後應該留下多少行(由列或新頁面引起)。 例如,如果我想避免在列的末尾留下一行,我會使用orphans
屬性,就像在排版中一樣,孤兒是單獨出現在頁面底部的段落的第一行該段的其餘部分被分到另一頁。 該屬性應該添加到正在分段的相同元素(在我們的例子中,multicol 容器)。
.container { column-count: 3; orphans: 2; }
要控制中斷後列或頁面頂部應有多少行,請使用widows
:
.container { column-count: 3; widows: 2; }
這些屬性處理行內框之間的中斷,例如段落中的單詞行。 因此,它們在標題或其他塊元素單獨位於列或頁面底部的情況下沒有幫助,您需要上面討論的中斷屬性。
盒子裝飾
最後一個可能感興趣的屬性是box-decoration-break
屬性。 這控制了您在兩個列框或頁面之間有一個邊框斷開的框的情況。 你想讓邊界基本上被切成兩半嗎? 還是您希望盒子的兩半中的每一半都完全包裹在邊框中?
第一種情況是默認情況,就像您將box-decoration-break
屬性設置為在盒子上slice
一樣。
.box { box-decoration-break: slice; }

要獲得第二種行為,請將box-decoration-break
設置為克隆。
.box { box-decoration-break: clone; }

瀏覽器支持分片
現在我們來看看我沒有上面的一堆 CodePen 示例來向您演示所有這些的原因,以及我寫這篇文章的主要原因。 瀏覽器對這些屬性的支持不是很好。
如果您在 Paged Media 中使用特定的用戶代理(例如 Prince)工作,那麼您可以享受對碎片的非常好的支持,並且可能會發現這些屬性非常有用。 如果您正在使用 web 瀏覽器,無論是在 multicol 中,創建打印樣式表,還是使用 Headless Chrome 之類的東西來生成 PDF,支持有點不完整。 你會發現支持最好的瀏覽器是 Edge——直到它遷移到 Chromium!

由於將碎片屬性與 multicol 混合在一起,然後為遺留屬性提供了一些單獨的數據,我可以使用它對解釋支持並沒有太大幫助。 因此,作為我為 MDN 記錄屬性及其支持所做的工作的一部分,我開始測試實際的瀏覽器支持。 以下是基於該測試的一些建議。
舊版和供應商前綴屬性
沒有歷史課我不能走得更遠。 如果您發現您確實需要對碎片的支持,那麼您可能會在最初是 CSS2 的一部分的遺留屬性中找到一些緩解(或在某些存在的前綴屬性中)。
在 CSS2 中,有一些屬性可以控制分頁。 Multicol 那時還不存在,所以唯一的碎片化上下文是分頁的。 這意味著引入了三個特定的分頁屬性:
-
page-break-before
-
page-break-after
-
page-break-inside
它們的工作方式類似於沒有page-
前綴的更通用的屬性,控制框之前、之後和內部的中斷。 對於打印樣式表,您會發現一些不支持新的break-
屬性的舊瀏覽器確實支持這些頁面前綴屬性。 這些屬性被視為新屬性的別名。
在 2005 年的 multicol 規範工作草案中詳細介紹了 multicol 的破壞屬性——使用以column-
為前綴的屬性(即column-break-before
、 column-break-after
和column-break-inside
)。 到 2009 年,這些都消失了,multicol 規範中出現了一個關於無前綴中斷屬性的草案,最終進入 CSS Fragmentation 規範。
但是,一些供應商前綴列特定的屬性是基於這些屬性實現的。 這些是:
-
-webkit-column-break-before
-
-webkit-column-break-after
-
-webkit-column-break-inside
支持 Multicol 中的分片
以下內容基於在多列上下文中測試這些特性。 我試圖解釋什麼是可能的,但請在您可用的任何瀏覽器中查看 CodePens。
Multicol 和break-inside
multicol 中的支持最適合break-inside
屬性。 Chrome、Firefox、Edge 和 Safari 的最新版本都支持break-inside: avoid
. 所以你應該發現在使用 multicol 時可以防止框在列之間斷開。
幾個瀏覽器,除了 Firefox,支持-webkit-column-break-inside
屬性,這可以與一個值一起使用, avoid
並且可以防止框在不支持break-inside
的列之間斷開。
Firefox 支持page-break-inside: avoid
在多列中。 因此,使用此屬性將防止 Firefox 65 之前的 Firefox 瀏覽器中的框內中斷。
這意味著,如果您想防止 multicol 中的框之間出現中斷,使用以下 CSS 將覆蓋盡可能多的瀏覽器,並儘可能回溯。
.box { -webkit-column-break-inside: avoid; page-break-inside: avoid; break-inside: avoid; }
至於column
值,明確指出您只想避免列之間而不是頁面之間的中斷,適用於除 Firefox 之外的所有瀏覽器。
下面的 CodePen 在 multicol 中匯總了其中一些測試,因此您可以自己嘗試它們。
請參閱 Pen Multicol Fragmentation Test:Rachel Andrew 的break-inside。
Multicol 和break-before
為了防止在元素之前中斷,您應該能夠使用break-before: avoid
或break-before: avoid-column
。 避免屬性沒有瀏覽器支持。
Edge 支持break-before: column
總是在元素的框之前強制中斷。
Safari、Chrome 和 Edge 也支持-webkit-column-break-before: always
,這將在元素的框之前強制中斷。 因此,如果你想在元素的框之前強制中斷,你應該使用:
.box { -webkit-column-break-before: always; break-before: column; }
防止在盒子前休息目前是一項不可能完成的任務。 您可以在下面使用這些屬性的一些示例:
請參閱 Pen Multicol Fragmentation Test:Rachel Andrew 的 break-before)。
Multicol 和break-after
為了防止在一個元素之後出現中斷,為了避免它成為列底部的最後一件事,您應該能夠使用break-after: avoid
和break-after: avoid-column
。 唯一支持這些的瀏覽器是 Edge。
Edge 還支持通過使用break-after: column
強制在元素後中斷,Chrome 支持break-after: column
和-webkit-column-break-after: always
。
Firefox 不支持break-after
或任何前綴屬性來強製或允許在框後中斷。
因此,除了 Edge 之外,您無法真正避免一個盒子後的中斷。 如果你想強制它們,你會在某些瀏覽器中使用以下 CSS 獲得結果:
.box { -webkit-break-after: always; break-after: column; }
請參閱 Pen Multicol Fragmentation Test:Rachel Andrew 的 break-after)。
從瀏覽器打印時的支持
無論您是直接從桌面瀏覽器打印,還是使用無頭 Chrome 或其他依賴瀏覽器技術的解決方案生成 PDF 文件,都沒有任何區別。 您依賴於瀏覽器對碎片屬性的支持。
如果您創建打印樣式表,您會發現對 break 屬性的支持與 multicol 類似; 但是,為了支持舊版瀏覽器,您應該將屬性加倍以使用以page-
為前綴的屬性。
打印樣式表和break-inside
在現代瀏覽器中, break-inside
屬性可用於防止框內中斷,添加page-break-inside
屬性以添加對舊瀏覽器的支持。
.box { page-break-inside: avoid; break-inside: avoid; }
打印樣式表和break-before
要在框之前強制中斷,請使用break-before:page
和page-break-before: always
。
.box { page-break-before: always; break-before: page; }
為了避免在盒子之前出現中斷,請使用break-before: avoid-page
和page-break-before: avoid
。
.box { page-break-before: avoid; break-before: avoid-page; }
與我們看到的等效多列值相比,對page
和avoid-page
值的支持更好。 大多數現代瀏覽器都支持。
打印樣式表和break-before
要在框後強制中斷,請使用break-after: page
和page-break-after: always
。
.box { page-break-after: always; break-after: page; }
為了防止在一個盒子之後出現中斷,請使用break-after: avoid-page
和page-break-after: avoid
。
.box { page-break-after: avoid; break-after: avoid-page; }
寡婦和孤兒
widows
和orphans
屬性享有良好的跨瀏覽器支持——唯一沒有實現的瀏覽器是 Firefox。 我建議在創建多列佈局或打印樣式表時使用這些。 如果他們因為某種原因不工作,你會得到寡婦和孤兒,這並不理想,但也不是災難。 如果它們確實有效,那麼您的排版看起來會更好。
盒子裝飾打破
box-decoration-break
的最後一個屬性在 Firefox 中支持 multicol 和 print。 Safari、Chrome 和其他基於 Chromium 的瀏覽器支持-webkit-box-decoration-break
,但僅限於內聯元素。 因此,例如,您可以克隆句子的邊界線; 在我們正在研究的情況下,他們沒有得到支持。
在下面的 CodePen 中,您可以看到測試-webkit-box-decoration-break: clone
with Feature Queries 返回 true; 但是,該屬性對 multicol 上下文中框的邊框沒有影響。
請參閱 Rachel Andrew 的 Pen Multicol:box-decoration-break。
使用分片
如您所見,目前瀏覽器的碎片化狀態有些碎片化! 也就是說,你可以達到一個合理的數量,如果它失敗了,結果往往不是最理想的,但不是災難。 這意味著值得一試。
值得注意的是,對這些屬性過於沉重可能會導致與您希望的結果不同。 如果您在網絡上工作,而不是在每個段落之後打印和強制分欄,那麼最終的段落比列的空間多,multicol 最終會在內聯方向溢出。 它將用完列來放置您的其他段落。 因此,即使有支持,您仍然需要仔細測試,並記住在很多情況下少即是多。
更多資源
要閱讀有關 MDN 屬性的更多信息,我最近更新了那裡的頁面,並且還試圖使瀏覽器兼容數據保持最新。 CSS Fragmentation 的主頁鏈接到各個屬性頁面,其中包含更多示例、瀏覽器兼容性數據和有關使用這些屬性的其他信息。