HTML API + CSS 控制頁面列印內容和樣式

陟上晴明發表於2023-02-22

本週來了一個新的需求,需要前端生成列印內容,每一項資料佔據一張 A4 紙,選擇多項就是分多張列印,所以需要列印指定內容區域,並且使用 page-break 來控制列印區域的分頁。
以前就只使用 CSS 控制過列印時樣式,隱藏一些不需要列印的區域,還嘗沒有試過列印指定區域內容,並且控制列印內容強制分頁,所以記錄一下。

選擇列印區域

區域性列印的方式據我瞭解到的有三種:

  1. 透過開始、結束標記來控制列印範圍;

    • <!--startprint--><!--endprint-->
  2. 透過把區域性內容賦值給body,列印整個頁面,而後再把原頁面內容重複覆蓋回來;
  3. 透過動態建立<iframe>來列印;
    window.print() 區域性列印三種方式 - 矽谷工具人



比較推薦的是第一種第三種方法。不過我選擇去 NPM 上找一個現成的輪子 ?。
其實也不是我找的,其實小夥伴再之前已經做完了區域性列印功能,只是後來提了新的需求這回要求列印內容的樣式,所以由我來接手了。
選擇的庫是 vue-print-nb,我看了一下原始碼其實現方式是上面提到的第三種。

首先透過 documentCreateElement('iframe') 建立一個 <iframe> 元素;再透過 document.getElementById() 拿到需要列印的區域內容,同時把獲取到需要列印的區域內容透過 Node.cloneNode() 複製到建立的 <iframe> 當中;最後用 window.print() 這個API開啟列印對話方塊列印當前檔案,就可以實現列印原先指定的區域內容了。

更詳細的方法可以檢視倉庫原始碼或者檢視文章尾部的參考資源中的對應文章,我這裡就不現學現賣了。

配置頁面列印樣式

透過 CSS 的 @page 規則可以控制列印時的頁面樣式,但是可配置的內容不多。

@page 規則用於在列印檔案時修改某些 CSS 屬性。你不能用 @page 規則來修改所有的 CSS 屬性,而是隻能修改 margin,orphans,widowpage breaks of the document。對其他屬性的修改是無效的。
@page - MDN



暫時我能用到的就只有 marginsize 以及 page-break 了,使用方式很簡單和正常的CSS一樣使用,例如:

@page {
    margin: 1cm; // 設定列印頁邊距;
    size: A4 portrait; // 指定列印時紙張大小和方向
    break-after: page; // 分頁屬性
}

可以把 @page 規則理解成列印時的物件去設定,並不和 @media print 相似。
並且我在嘗試使用 size 指定紙張的大小時(別名), ChromeFireFoxEdge 都沒有生效。指定紙張方向是可以的。

  • 使用別名指定紙張大小時, Edge 中甚至影響到了 margin 屬性的生效,所以最好 size 只指定方向 (2022 年 7 月 25 日)。
  • 使用具體數值指定紙張大小時,ChromeFireFox 中指定的紙張方向失效了 (2022 年 7 月 25 日)。

簡單配置一下頁面列印樣式,然後開始寫列印內容樣式。因為實際上可以配置的屬性真的不多,而且各瀏覽器支援的程度也參差不齊,如果高定製化的話會很容易自閉。

頁面內容樣式

這塊內容就是正常寫CSS樣式了,有一部分在列印時的特殊樣式搭配使用 @media print 來匹配就可以了,一遍調整一邊使用瀏覽器的列印預覽功能檢視實際效果即可,所以就不贅述了。

分頁列印內容

分頁的話,使用在前文中提到的 page-break,給佔據整頁/整塊區域的內容增加 break-before 或者 break-after 屬性即可。
也可以給單獨的類似 <hr /> 這樣的水平線元素增加 page-break 屬性。然後再搭配 @media print 給水平線在列印時隱藏,這樣的話可以在檢視頁面的時候就知道哪些部分他會分頁,並且在列印時也不會印象列印內容排版。

注意:如果要在列印時隱藏 <hr/> 元素並且保持分頁功能,需要使用 visibility: hidden; 來隱藏,不能使用 display:none 不然分頁屬性不會生效。

? 遇到問題

1. 使用 page-break-after 是內容分頁但在列印時不生效

因為 page-break-afterbreak-after 替代了。相對應的 page-break-before 也被 break-before 屬性替代了。
也有可能是因為你的值設定錯了,設定為 page 就好了。

  • 常規中斷值有:auto,avoid,always,all;
  • 分頁使用的值有:avoid-page,page,left,right,recto,verso;
  • 分欄使用的值有:avoid-column,column;
  • 分割槽使用的值有:avoid-region,region;
    《CSS新世界》

我看張鑫旭大佬說 region 相關的屬性現代瀏覽器已經不再支援了,所以可以忽略。

2. 設定的背景色沒有被列印。

因為預設列印頁面時為了節約墨水,所以預設情況下是不會列印背景色的,如果希望列印的時候保留背景色可以只用 color-adjust 屬性來控制。

color-adjust 屬性並非一個標準屬性,所以在使用時需要檢視一下各瀏覽器的相容程度。有的瀏覽器需要增加私有字首(比如 -webkit-print-color-adjust)。

3. 列印頁面的時候,在列印預覽視窗選擇了彩色模式但是實際列印出來還是黑白的。

瀏覽器彈出的列印預覽設定了彩色模式,並不一定代表了印表機設定的首選項內是彩色的,可以開啟對應的印表機檢視首選項設定內的色彩模式是否為彩色。
具體步驟為:

  1. 在列印預覽彈窗內點選 使用系統對話方塊列印

    • 如果沒有該項,可在瀏覽器中使用 Ctrl+Shift+P 的組合鍵開啟
  2. 在彈出的列印視窗中找到對應的印表機裝置,點選首選項
  3. 選擇紙張/輸出卡片,找到色彩模式修改為彩色
  4. 最後點選確定儲存並應用設定,再點選列印嘗試列印;

尾聲

現在很多和列印相關的規則和API還都是草案,很多規則都沒有固定下來成為標準,不同的瀏覽器也有自己的實現方式,有些屬性還是部分支援(比如:size 屬性)或者需要私有字首。
儘量先檢視一下最新的檔案,不要照搬筆記或者文章,因為誰也不知道啥時候就改變了,我在寫這篇筆記時看到草案的修訂時間是 2022年5月24日,不知道你們看到的時候會是第幾個版本了。

那就這樣吧,以上。

? 參考檔案

Paged media - CSS | MDN
@page - CSS | MDN
size - CSS | MDN
page-break-after - CSS | MDN
break-after - CSS | MDN
print-color-adjust - CSS | MDN
window.print - Web API | MDN
CSS Paged Media Module Level 3 | W3C Editor's Draft

page-break | CSS-Tricks - CSS-Tricks
Can I force a page break in HTML printing? - Stack Overflow
CSS 列印 - SegmentFault 思否
記CSS中break-after的一個坑 - 知乎
window.print()區域性列印三種方式 - 矽谷工具人 - 部落格園
window.print()列印時根據頁面高度設定居中顯示、設定列印佈局(縱向、橫向)\_清雲青雲的部落格 - CSDN

本文參與了SegmentFault 思否寫作挑戰賽,歡迎正在閱讀的你也加入。

相關文章