简体   繁体   English

如何在单击按钮时打印 React 组件?

[英]How to print React component on click of a button?

How can I print only one component on click of a button.如何在单击按钮时只打印一个组件。

I know this solution:我知道这个解决方案:

window.frames["print_frame"].window.focus();
window.frames["print_frame"].window.print();
$('.print_frame').remove();

But React doesn't want to work with a frame.但是 React 不想使用框架。

Any solutions?有什么解决办法吗? Thank you.谢谢你。

There is kind of two solutions on the client.客户端有两种解决方案。 One is with frames like you posted.一个是像你发布的框架。 You can use an iframe though:您可以使用 iframe:

var content = document.getElementById("divcontents");
var pri = document.getElementById("ifmcontentstoprint").contentWindow;
pri.document.open();
pri.document.write(content.innerHTML);
pri.document.close();
pri.focus();
pri.print();

This expects this html to exist这期望这个 html 存在

<iframe id="ifmcontentstoprint" style="height: 0px; width: 0px; position: absolute"></iframe>

The other solution is to use the media selector and on the media="print" styles hide everything you don't want to print.另一种解决方案是使用媒体选择器,并在media="print"样式上隐藏您不想打印的所有内容。

<style type="text/css" media="print">
   .no-print { display: none; }
</style>

Last way requires some work on the server.最后一种方式需要在服务器上进行一些工作。 You can send all the HTML+CSS to the server and use one of many components to generate a printable document like PDF.您可以将所有 HTML+CSS 发送到服务器,并使用众多组件之一来生成 PDF 等可打印文档。 I've tried setups doing this with PhantomJs.我已经尝试过使用 PhantomJs 进行设置。

I was looking for a simple package that would do this very same task and did not find anything so I created https://github.com/gregnb/react-to-print我正在寻找一个简单的包来完成同样的任务并且没有找到任何东西所以我创建了https://github.com/gregnb/react-to-print

You can use it like so:你可以像这样使用它:

 <ReactToPrint
   trigger={() => <a href="#">Print this out!</a>}
   content={() => this.componentRef}
 />
 <ComponentToPrint ref={el => (this.componentRef = el)} />

You'll have to style your printout with @media print {} in the CSS but the simple code is:您必须在 CSS 中使用@media print {}设置打印输出的样式,但简单的代码是:

export default class Component extends Component {

    print(){
        window.print();
    }


  render() {

  ...
  <span className="print"
              onClick={this.print}>
    PRINT
    </span>

  } 
}

Hope that's helpful!希望这会有所帮助!

On 6/19/2017 This worked perfect for me. 2017 年 6 月 19 日,这对我来说非常完美。

import React, { Component } from 'react'

class PrintThisComponent extends Component {
  render() {
    return (
      <div>
        <button onClick={() => window.print()}>PRINT</button>
        <p>Click above button opens print preview with these words on page</p>
      </div>
    )
  }
}

export default PrintThisComponent

If you're looking to print specific data that you already have access to, whether it's from a Store, AJAX, or available elsewhere, you can leverage my library react-print.如果您希望打印您已经可以访问的特定数据,无论是来自商店、AJAX 还是其他地方,您都可以利用我的库 react-print。

https://github.com/captray/react-print https://github.com/captray/react-print

It makes creating print templates much easier (assuming you already have a dependency on react).它使创建打印模板变得更加容易(假设您已经依赖于 react)。 You just need to tag your HTML appropriately.您只需要适当地标记您的 HTML。

This ID should be added higher up in your actual DOM tree to exclude everything except the "print mount" below.这个 ID 应该在你的实际 DOM 树中添加到更高的位置,以排除除了下面的“打印安装”之外的所有内容。

<div id="react-no-print"> 

This is where your react-print component will mount and wrap your template that you create:这是您的 react-print 组件将安装和包装您创建的模板的地方:

<div id="print-mount"></div>

An example looks something like this:一个示例如下所示:

var PrintTemplate = require('react-print');
var ReactDOM = require('react-dom');
var React = require('react');

var MyTemplate = React.createClass({
    render() {
        return (
            <PrintTemplate>
                <p>Your custom</p>
                <span>print stuff goes</span>
                <h1>Here</h1>
            </PrintTemplate>
        );
    }
});

ReactDOM.render(<MyTemplate/>, document.getElementById('print-mount'));

It's worth noting that you can create new or utilize existing child components inside of your template, and everything should render fine for printing.值得注意的是,您可以在模板中创建新的或利用现有的子组件,并且所有内容都应该可以很好地打印。

The solution provided by Emil Ingerslev is working fine, but CSS is not applied to the output. Emil Ingerslev 提供的解决方案工作正常,但 CSS 未应用于输出。 Here I found a good solution given by Andrewlimaza .在这里,我找到了Andrewlimaza给出的一个很好的解决方案。 It prints the contents of a given div, as it uses the window object's print method, the CSS is not lost.它打印给定 div 的内容,因为它使用 window 对象的 print 方法,所以 CSS 不会丢失。 And there is no need for an extra iframe also.而且也不需要额外的 iframe。

var printContents = document.getElementById("divcontents").innerHTML;
var originalContents = document.body.innerHTML;
document.body.innerHTML = printContents;
window.print();
document.body.innerHTML = originalContents;

Update 1 : There is unusual behavior, in chrome/firefox/opera/edge, the print or other buttons stopped working after the execution of this code.更新 1 :在 chrome/firefox/opera/edge 中存在异常行为,执行此代码后打印或其他按钮停止工作。

Update 2 : The solution given is there on the above link in comments:更新 2 :给出的解决方案在上面的评论链接中:

.printme { display: none;}
@media print { 
    .no-printme  { display: none;}
    .printme  { display: block;}
}

<h1 class = "no-printme"> do not print this </h1>    
<div class='printme'>
  Print this only 
</div>    
<button onclick={window.print()}>Print only the above div</button>

First want to credit @emil-ingerslev for an awesome answer.首先要感谢@emil-ingerslev 的出色答案。 I tested it and it worked perfectly.我对其进行了测试,并且效果很好。 There were two things however I wanted to improve.有两件事我想改进。

  1. I didn't like having to already have <iframe id="ifmcontentstoprint" style="height: 0px; width: 0px; position: absolute"></iframe> already in the dom tree.我不喜欢<iframe id="ifmcontentstoprint" style="height: 0px; width: 0px; position: absolute"></iframe>已经在 dom 树中。
  2. I wanted to create a way to make it reusable.我想创造一种使其可重复使用的方法。

I hope this makes others happy and saves a few minutes of life.我希望这能让其他人开心,并挽救几分钟的生命。 Now go take those extra minutes and do something nice for someone.现在去多花点时间,为某人做点好事。

function printPartOfPage(elementId, uniqueIframeId){
    const content = document.getElementById(elementId)
    let pri
    if (document.getElementById(uniqueIframeId)) {
        pri = document.getElementById(uniqueIframeId).contentWindow
    } else {
        const iframe = document.createElement('iframe')
        iframe.setAttribute('title', uniqueIframeId)
        iframe.setAttribute('id', uniqueIframeId)
        iframe.setAttribute('style', 'height: 0px; width: 0px; position: absolute;')
        document.body.appendChild(iframe)
        pri = iframe.contentWindow
    }
    pri.document.open()
    pri.document.write(content.innerHTML)
    pri.document.close()
    pri.focus()
    pri.print()
}

EDIT 2019-7-23: After using this more, this does have the downside that it doesn't perfectly render react components.编辑 2019-7-23:在使用更多之后,这确实有缺点,它不能完美地渲染反应组件。 This worked for me when the styling was inline but not when handled by styled-components or some other situations.当样式是内联时,这对我有用,但在样式组件或其他一些情况下处理时不起作用。 If I come up with a foolproof method I will update.如果我想出一个万无一失的方法,我会更新。

Just sharing what worked in my case as someone else might find it useful.只是分享对我有用的东西,因为其他人可能会觉得它有用。 I have a modal and just wanted to print the body of the modal which could be several pages on paper.我有一个模态,只是想打印模态的正文,它可能是纸上的几页。

Other solutions I tried just printed one page and only what was on screen.我尝试的其他解决方案只打印了一页,并且只打印了屏幕上的内容。 Emil's accepted solution worked for me:埃米尔接受的解决方案对我有用:

https://stackoverflow.com/a/30137174/3123109 https://stackoverflow.com/a/30137174/3123109

This is what the component ended up looking like.这就是组件最终的样子。 It prints everything in the body of the modal.它打印模态正文中的所有内容。

import React, { Component } from 'react';
import {
    Button,
    Modal,
    ModalBody,
    ModalHeader
} from 'reactstrap';

export default class TestPrint extends Component{
    constructor(props) {
        super(props);
        this.state = {
            modal: false,
            data: [
                'test', 'test', 'test', 'test', 'test', 'test', 
                'test', 'test', 'test', 'test', 'test', 'test', 
                'test', 'test', 'test', 'test', 'test', 'test',
                'test', 'test', 'test', 'test', 'test', 'test',
                'test', 'test', 'test', 'test', 'test', 'test',
                'test', 'test', 'test', 'test', 'test', 'test',
                'test', 'test', 'test', 'test', 'test', 'test',
                'test', 'test', 'test', 'test', 'test', 'test'            
            ]
        }
        this.toggle = this.toggle.bind(this);
        this.print = this.print.bind(this);
    }

    print() {
        var content = document.getElementById('printarea');
        var pri = document.getElementById('ifmcontentstoprint').contentWindow;
        pri.document.open();
        pri.document.write(content.innerHTML);
        pri.document.close();
        pri.focus();
        pri.print();
    }

    renderContent() {
        var i = 0;
        return this.state.data.map((d) => {
            return (<p key={d + i++}>{i} - {d}</p>)
        });
    }

    toggle() {
        this.setState({
            modal: !this.state.modal
        })
    }

    render() {
        return (
            <div>
                <Button 
                    style={
                        {
                            'position': 'fixed',
                            'top': '50%',
                            'left': '50%',
                            'transform': 'translate(-50%, -50%)'
                        }
                    } 
                    onClick={this.toggle}
                >
                    Test Modal and Print
                </Button>         
                <Modal 
                    size='lg' 
                    isOpen={this.state.modal} 
                    toggle={this.toggle} 
                    className='results-modal'
                >  
                    <ModalHeader toggle={this.toggle}>
                        Test Printing
                    </ModalHeader>
                    <iframe id="ifmcontentstoprint" style={{
                        height: '0px',
                        width: '0px',
                        position: 'absolute'
                    }}></iframe>      
                    <Button onClick={this.print}>Print</Button>
                    <ModalBody id='printarea'>              
                        {this.renderContent()}
                    </ModalBody>
                </Modal>
            </div>
        )
    }
}

Note: However, I am having difficulty getting styles to be reflected in the iframe .注意:但是,我很难让样式反映在iframe中。

Perfect solution:完美解决方案:

class PrintThisComponent extends Component {
  render() {
    return (
      <div>
      <button className="btn btn-success btn-lg"
        onClick={() => window.print()}>
        PRINT
      </button>
      </div>
    )
  }
}
export default PrintThisComponent;

Just go anywhere in your React file and do:只需在 React 文件中的任何地方执行以下操作:

import PrintThisComponent from 'the file having above code';

<PrintThisComponent />

如果任何人仍然对打印组件有问题,请使用此库https://www.npmjs.com/package/react-to-print

What's Up!这是怎么回事! I made this... maybe it help someone我做了这个......也许它可以帮助某人

It will wait to be loaded to print, It's the magic... Dont forget to change the CSS link它将等待加载打印,这很神奇...不要忘记更改 CSS 链接


    /**
     * PRINT A DIV ELEMENT
     * @param {HTMLDivElement} div
     */
    function print(div) {
        let innerHTML = `<!DOCTYPE html>
        <html lang="pt-BR">
        <head>   
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
        <link href="/css/app.css" rel="stylesheet" />
        </head>
        <body>`;
        innerHTML += div.innerHTML + "</body></html>";
        console.log(innerHTML);
        let fakeIFrame = window.document.createElement("iframe");
        document.body.appendChild(fakeIFrame);
        let fakeContent = fakeIFrame.contentWindow;
        fakeContent.document.open();
        fakeContent.document.write(innerHTML);
        fakeContent.document.close();
        fakeContent.focus();
        fakeIFrame.addEventListener("load", () => {
            fakeContent.print();
        });
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM