前端打印插件TS版(支持vue,react)
把之前的插件转成了ts版本,这里感谢ai的帮忙,让我省了很多事情,不多说,上代码:
interface Options { noPrint?: string; title?:string; printComplete?:Function; } export default class Print { private options: Options; private dom: HTMLElement; private delay:number = 500; private iframe!:HTMLIFrameElement; constructor(dom: string | HTMLElement, options: Options={noPrint:".no-print",title:"打印"}) { this.options = options // if ((typeof dom) === "string") { // this.dom = document.getElementById(dom as string) as HTMLElement; // } else { // this.isDOM(dom) // this.dom = this.isDOM(dom) ? dom as HTMLElement : (dom as any).$el; // } this.dom = this.getDom(dom); this.init(); } private getDom(dom: string | HTMLElement):HTMLElement { let _dom; if ((typeof dom) === "string") { _dom = document.getElementById(dom as string) as HTMLElement; } else { this.isDOM(dom) _dom = this.isDOM(dom) ? dom as HTMLElement : (dom as any).$el; } return _dom; } public static start(dom: string | HTMLElement, options?: Options) { return new Print(dom,options); } private init(): void { let content = this.getStyle() + this.getHtml(); this.writeIframe(content); } private getStyle(): string { let str = "", styles = document.querySelectorAll('style,link'); for (let i = 0; i < styles.length; i++) { str += styles[i].outerHTML; } str += "<style>" + (this.options.noPrint ? this.options.noPrint : '.no-print') + "{display:none!important;}</style>"; return str; } private getHtml(): string { let inputs = document.querySelectorAll('input'); let textareas = document.querySelectorAll('textarea'); let selects = document.querySelectorAll('select'); for (let 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 (let k2 = 0; k2 < textareas.length; k2++) { if (textareas[k2].type == 'textarea') { textareas[k2].innerHTML = textareas[k2].value } } for (let k3 = 0; k3 < selects.length; k3++) { if (selects[k3].type == 'select-one') { let child = selects[k3].children; for (let i in child) { if (child[i].tagName == 'OPTION') { if (child[i]['selected'] == true) { child[i].setAttribute('selected', "selected") } else { child[i].removeAttribute('selected') } } } } } let _dom = this.wrapperRefDom(this.dom); if(_dom) { let outerHTML = _dom.outerHTML; return outerHTML; } else { return '' } } // 向父级元素循环,包裹当前需要打印的元素 // 防止根级别开头的 css 选择器不生效 private wrapperRefDom(refDom: HTMLElement): HTMLElement | null{ let prevDom: HTMLElement | null = null let currDom: HTMLElement | null = refDom // 判断当前元素是否在 body 中,不在文档中则直接返回该节点 if (!this.isInBody(currDom)) return currDom while (currDom) { if (prevDom) { let element = currDom.cloneNode(false) as HTMLElement element.appendChild(prevDom) prevDom = element } else { prevDom = currDom.cloneNode(true) as HTMLElement } let list; // @ts-ignore if(currDom.parentElement.classList) { // @ts-ignore list= currDom.parentElement.classList.value.split(' ') } if (list&&list.indexOf('no-print-root') !== -1) { currDom = null; } else { currDom = currDom.parentElement } } return prevDom } private writeIframe(content: string): void { let w: Window, doc: Document, iframe:HTMLIFrameElement = document.createElement('iframe'), f = document.body.appendChild(iframe); iframe.id = "myIframe"; iframe.setAttribute('style', 'position:absolute;width:0;height:0;top:-10px;left:-10px;z-index:1000'); // iframe.setAttribute('style', 'position:absolute;width:1920px;height:1080px;top:0;left:0;z-index:1000'); w = f.contentWindow || f.contentDocument as any; doc = f.contentDocument || f.contentWindow!.document; doc.open(); doc.write(content); doc.close(); iframe.onload = () =>{ this.toPrint(w); setTimeout(function () { document.body.removeChild(iframe) }, this.delay) } this.iframe = iframe; } private toPrint(frameWindow: Window): void { try { let _title = document.title; document.title = this.options.title as string; setTimeout( () =>{ frameWindow.focus(); try { if (!frameWindow.document.execCommand('print', false, undefined)) { frameWindow.print(); } } catch (e) { frameWindow.print(); } frameWindow.close(); document.title = _title; if(this.options.printComplete) { this.options.printComplete(this.iframe) } }, 10); } catch (err) { console.log('err', err); } } private isInBody(node: Node): boolean { return (node === document.body) ? false : document.body.contains(node); } private isDOM(obj: any): boolean { return (typeof HTMLElement === 'object') ? obj instanceof HTMLElement : obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string'; } }
使用:Print.start('domId');
注意:
1.不要打印的加no-print的class
2.如果不想往上冒泡的加no-print-root的class
(有时候打印的东西太长,你需要打印滚动容器内的内容,但是一般的打印插件会把整个父给copy下来,导致把滚动容器也打印出来,最后打印的是你看到的页面,滚动容器遮住的部分打印不出来,用no-print-root可以有效的防止这点,而且比我js写的那版需要穿父类参数的简单的多)
匿名
123
匿名
1