Recently received a demand, it involves web printing, thought the browser’s own execution window. The print () method of obtaining the print control, didn’t think it is not so simple, not only in the print preview style is wrong, even can’t show the whole content, more than the content of the page only shows a page, it obviously can’t meet the demand of project!
Therefore, relevant optimization schemes are studied and sorted out as follows:
Window.print () default effect defect
1. Print control is not paginated by default, only one page is displayed
2. Dom layouts and styles can easily be misplaced and lost
3. I want to print locally, but the default is to get the entire body.innerHTML content
Print style optimization
This is done by adding specific CSS code for printers and previews to adjust the effect. CSS is introduced in the following ways
Four ways to load the print style
1.<link rel="stylesheet" href="" media="print">
2.@import url print
Overview @import CSS@ rules for importing style rules from other style sheets. These rules must precede all other types of rules except the @charset rule; Because it is not a nested statement, @import cannot be used in the rules of conditional groups. Thus, user agents can avoid retrieving resources for unsupported media types, and authors can specify media-dependent @import rules. These conditions import comma-separated media queries specified after the URI. In the absence of any media queries, the import is unconditional. Specifies that all media have the same effect. Grammar @ import url; @import url list-of-media-queries;Copy the code
Media =”print”
4. Query information through @media
Remove header footer
<style> @media print { @page { margin: 0; // You can control the print layout (four margins)} body {border: 1px solid #999; } } </style>Copy the code
@ page introduction
The @Page rule is used to modify certain CSS properties when printing a document. Orphans,widow, and Page breaks of the document You may not modify all CSS attributes using the @page rule, but rather may modify margin,orphans,widow, and Page breaks of the document. Changes to other attributes are not valid.
@page {
margin: 1cm;
}
@page :first {
margin: 2cm;
}
Copy the code
Control the paging
Page-break-before: Controls whether to page before the specified element
Page-break-after: Controls whether paging occurs after the specified element
Page-break-inside Controls whether page breaks can be inserted in the specified element
Optional parameters: always | auto | get | left | right | inherit
Example:
<style>
@media print {
@page {
margin: 0;
}
body {
border: 1px solid #999;
}
p {
page-break-after: always;
}
}
</style>
Copy the code
Package print.js to implement local printing
Since we’re printing the contents of the body, we can manually create a DOM element that replaces the body when we call print(), which has two lifecycle hooks, Beforeprint and AfterPrint, replace the DOM before printing to print the DOM I want, and then restore the PREVIOUS DOM.
I found a case study on Github and tested it
/ / https://github.com/xyl66/vuePlugs_printjs/blob/master/print.js / / Print the class attribute, method definition / * eslint - disable * / const Print = function (dom, options) { if (! (this instanceof Print)) return new Print(dom, options); this.options = this.extend({ 'noPrint': '.no-print' }, options); if ((typeof dom) === "string") { this.dom = document.querySelector(dom); } else { this.isDOM(dom) this.dom = this.isDOM(dom) ? dom : dom.$el; } this.init(); }; Print.prototype = { init: function () { var content = this.getStyle() + this.getHtml(); this.writeIframe(content); }, extend: function (obj, obj2) { for (var k in obj2) { obj[k] = obj2[k]; } return obj; }, getStyle: function () { var str = "", styles = document.querySelectorAll('style,link'); for (var i = 0; i < styles.length; i++) { str += styles[i].outerHTML; } str += "<style>" + (this.options.noPrint ? this.options.noPrint : '.no-print') + "{display:none; }</style>"; return str; }, getHtml: function () { var inputs = document.querySelectorAll('input'); var textareas = document.querySelectorAll('textarea'); var selects = document.querySelectorAll('select'); for (var k = 0; k < inputs.length; k++) { if (inputs[k].type == "checkbox" || inputs[k].type == "radio") { if (inputs[k].checked == true) { inputs[k].setAttribute('checked', "checked") } else { inputs[k].removeAttribute('checked') } } else if (inputs[k].type == "text") { inputs[k].setAttribute('value', inputs[k].value) } else { inputs[k].setAttribute('value', inputs[k].value) } } for (var k2 = 0; k2 < textareas.length; k2++) { if (textareas[k2].type == 'textarea') { textareas[k2].innerHTML = textareas[k2].value } } for (var k3 = 0; k3 < selects.length; k3++) { if (selects[k3].type == 'select-one') { var child = selects[k3].children; for (var i in child) { if (child[i].tagName == 'OPTION') { if (child[i].selected == true) { child[i].setAttribute('selected', "Selected ")} else {child[I].removeAttribute('selected')}}}}} https://github.com/xyl66/vuePlugs_printjs/issues/36 let outerHTML = this.wrapperRefDom(this.dom).outerHTML return outerHTML; }, // loop through the parent element to wrap the current element that needs to be printed // prevent root CSS selectors from not working wrapperRefDom: Function (refDom) {let prevDom = null let currDom = refDom // If (! this.isInBody(currDom)) return currDom while (currDom) { if (prevDom) { let element = currDom.cloneNode(false) element.appendChild(prevDom) prevDom = element } else { prevDom = currDom.cloneNode(true) } currDom = currDom.parentElement } return prevDom }, writeIframe: function (content) { var w, doc, iframe = document.createElement('iframe'), f = document.body.appendChild(iframe); iframe.id = "myIframe"; //iframe.style = "position:absolute; width:0; height:0; top:-10px; left:-10px;" ; iframe.setAttribute('style', 'position:absolute; width:0; height:0; top:-10px; left:-10px; '); w = f.contentWindow || f.contentDocument; doc = f.contentDocument || f.contentWindow.document; doc.open(); doc.write(content); doc.close(); var _this = this iframe.onload = function(){ _this.toPrint(w); setTimeout(function () { document.body.removeChild(iframe) }, 100) } }, toPrint: function (frameWindow) { try { setTimeout(function () { frameWindow.focus(); try { if (! frameWindow.document.execCommand('print', false, null)) { frameWindow.print(); } } catch (e) { frameWindow.print(); } frameWindow.close(); }, 10); } catch (err) { console.log('err', err); }}, // Check if an element is a descendant of the body element and not the body element itself isInBody: function (node) {return (node === document.body)? false : document.body.contains(node); }, isDOM: (typeof HTMLElement === 'object') ? function (obj) { return obj instanceof HTMLElement; } : function (obj) { return obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string'; }}; const MyPlugin = {} MyPlugin.install = function (Vue, options) { // 4. Prototype.$print = print} export default MyPluginCopy the code
Its more than 100 lines have achieved the effect described just now, but its function is relatively simple, and it does not support the configuration of some common parameters such as custom style overwriting. Moreover, I found a bug, that is, 2 pages are displayed by default, but there is very little content at this time, resulting in another blank page. Nevertheless can expand on this foundation, still good.
CLODOP print control
It is said to be very powerful and can print silently, but does not support MAC OS, pass website
Ultimate solution: printJS
This is the solution I finally settled on. Compared to the simple JS wrapper function above, it provides more configuration and supports PDF, HTML, image, JSON, raw-HTML printing.
Making the address
It’s also easy to use:
Prototype.$printJS = printJS // HTML $printJS({printable: 'elementId',type: 'HTML ', : ['*']})Copy the code
I once encountered a bug in this scheme when USING it, because I used the image format and converted webpage elements into PNG images based on HTML-to-image, and then used it
this.$printJS({printable: image-url,type: 'image'})
Copy the code
To print, there is an advantage of using images to print, is the style will not be misaligned, but there is an error: CSS cross-domain, the problem is the HTML-to-image plug-in, do not use this plug-in will not have this bug.
CSS cross-domain is often due to the use of the LINK tag inside JS to introduce CSS styles. With the increasingly strict security requirements of browsers, it is necessary to configure crossOrigin=”anonymous” on the link.
There is another problem. When I use the Intranet CDN, all CSS styles are lost in the print preview. I don’t know the reason yet, so I give up using the CDN form first.
conclusion
The function of web page printing has been very common in some IMS systems. The above several optimization schemes are the results of temporary research after recent encounter, which have met our project requirements. There may be some deficiencies, so they are only for reference.
Collection: My Github blog and case source code
Team blog: SAIC Insurance Blog