部落格地址jsonz1993.github.io/2018/06/win…
近日有個需求是做頁面列印的,趁這個機會補一下比較冷門的瀏覽器列印知識。本文只討論 Chrome、Safari、Firefox瀏覽器的情況。
列印介面
首先瀏覽器列印是一個很成熟的應用~ 至少是很早就已經有應用的功能,所以不會有什麼相容問題
最簡單的列印就是直接呼叫 window.print()
,當然用 document.execCommand(`print`)
也可以達到同樣的效果。
這時候在Safari和Chrome都會彈起列印預覽的視窗,FireFox沒有預覽而是直接讓你選擇印表機,OSx下可以通過預覽PDF來預覽~
一般這種直接在網頁上呼叫 print 的方法是沒辦法滿足我們的業務需求,比如說:
- 調整佈局和字型大小來適應A4紙
- 列印的時候用不同的樣式風格
- 使用更高清的圖片來列印
- 某一些不相關的東西不出現在列印中等等等等
那麼有哪些方法可以幫助我們改善列印的使用者體驗呢?
使用 print style sheet (列印樣式表)
我們可以在 link 上加上一個 media=”print” 來標識這是印表機才會應用的樣式表, 如:
<link href="/example.css" media="print" rel="stylesheet" />
複製程式碼
這樣列印的時候,就會預設將該樣式表應用到文件中
使用媒介查詢
相容性: IE9+ 其他主流瀏覽器都支援
當我們要修改的樣式沒有那麼多的時候,其實完全不需要重新寫個樣式表,只要寫上一個媒介查詢也可以達到同樣的效果,如:
h1 {
font-size: 14px;
}
@media print {
h1 {
font-size: 20px;
}
}
複製程式碼
事件監聽
beforeprint && afterprint
有兩個事件可以監聽到到列印事件,一個是beforeprint
,一個是afterprint
,分別表示列印事件觸發前後。
這個事件在 IE6 就已經支援了,不過一點都不驚訝~ 畢竟IE很早就支援很多介面呼叫,之前好像做過IE開啟Excel的需求~
相容大概是 Firefox、IE全支援, Chrome63+支援, Safari暫不支援,算是一半一半吧。
window.addEventListener(`beforeprint`, ()=> {
document.body.innerHTML = `正在列印...`;
});
window.addEventListener(`afterprint`, ()=> {
document.body.innerHTML = `列印完成...`;
});
複製程式碼
window.matchMedia 測試媒體查詢介面
如果你想要相容Safari或許可以試一下 window.matchMedia
相容是 IE10+,其他主流瀏覽器完全沒問題。
這個的用法稍微有點不一樣,首先建立一個MediaQueryList物件,再通過他監聽變化,如:
const printMedia = window.matchMedia(`print`);
function printChange({ matches, }) {
document.body.innerHTML = matches? `正在列印...`: `列印完成/取消`;
}
printMedia.addListener(printChange);
複製程式碼
更加個性化定製列印區域/列印內容
如果專案上用的是jq等,或者想簡單粗暴的列印某個區域又不想重新寫樣式表啊,什麼的。
最傻瓜版的方式就是直接用jq外掛 jQuery.print
也可以自己寫一個去處理,大概的思路是建立一個iframe,把要列印的dom和樣式表都丟進去,再呼叫iframe的列印事件。 這裡寫一個簡單的 demo
function printPartial(dom, { title= document.title,}= {}) {
if (!dom) return;
let copyDom = document.createElement(`span`);
const styleDom = document.querySelectorAll(`style, link, meta`);
const titleDom = document.createElement(`title`);
titleDom.innerText = title;
copyDom.appendChild(titleDom);
Array.from(styleDom).forEach(item=> {
copyDom.appendChild(item.cloneNode(true));
});
copyDom.appendChild(dom.cloneNode(true));
const htmlTemp = copyDom.innerHTML;
copyDom = null;
const iframeDom = document.createElement(`iframe`);
const attrObj = {
height: 0,
width: 0,
border: 0,
wmode: `Opaque`
};
const styleObj = {
position: `absolute`,
top: `-999px`,
left: `-999px`,
};
Object.entries(attrObj).forEach(([key, value])=> iframeDom.setAttribute(key, value));
Object.entries(styleObj).forEach(([key, value])=> iframeDom.style[key] = value);
document.body.insertBefore(iframeDom, document.body.children[0]);
const iframeWin = iframeDom.contentWindow;
const iframeDocs = iframeWin.document;
iframeDocs.write(`<!doctype html>`);
iframeDocs.write(htmlTemp);
iframeWin.focus();
iframeWin.print();
document.body.removeChild(iframeDom);
}
printPartial(document.querySelector(`#description`));
複製程式碼
最後一些注意的事情
- 列印會列印document下所有可見元素, 包括
<header>
裡面的 - 背景都不會被列印出來,包括背景色啊背景圖片啊等等
- 如果圖片是懶載入的,需要特殊處理,不然列印的時候會直接空白
參考: