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

How to print React component on click of a button?

如何在单击一个按钮时只打印一个组件。

我知道这个解决方案:

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

但是 React 不想使用框架。

有什么解决办法吗?谢谢。

客户端有两种解决方案。一个是像你发布的框架。不过,您可以使用 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();

这预计 html 存在

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

另一个解决方案是使用媒体选择器并在 media="print" 样式上隐藏您不想打印的所有内容。

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

最后一种方法需要在服务器上做一些工作。您可以将所有 HTML+CSS 发送到服务器并使用众多组件之一生成可打印文档,如 PDF。我已经尝试使用 PhantomJs 进行设置。

您必须在 CSS 中使用 @media print {} 设置打印输出的样式,但简单的代码是:

export default class Component extends Component {

    print(){
        window.print();
    }


  render() {

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

  } 
}

希望对您有所帮助!

如果您要打印您已经有权访问的特定数据,无论是来自商店、AJAX 还是其他地方可用,您都可以利用我的库 react-print。

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

它使创建打印模板变得更加容易(假设您已经依赖于 React)。您只需要适当地标记您的 HTML 即可。

此 ID 应添加到您实际的 DOM 树中更高的位置,以排除除下方 "print mount" 之外的所有内容。

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

这是你的 react-print 组件将挂载和包装你创建的模板的地方:

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

示例如下所示:

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'));

值得注意的是,您可以在模板中创建新的或利用现有的子组件,并且所有内容都应该可以正常打印。

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

我一直在寻找一个简单的包来完成同样的任务,但没有找到任何东西,所以我创建了 https://github.com/gregnb/react-to-print

你可以这样使用它:

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

只是分享在我的案例中有用的东西,因为其他人可能会发现它有用。我有一个模态,只是想打印模态的主体,它可能是纸上的几页。

我尝试过的其他解决方案只打印了一页,而且只打印了屏幕上显示的内容。 Emil 接受的解决方案对我有用:

这就是组件最终的样子。它打印模态主体中的所有内容。

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>
        )
    }
}

注意:但是,我很难在 iframe 中反映样式。

首先要感谢@emil-ingerslev 的精彩回答。我对其进行了测试,并且效果很好。但是有两件事我想改进。

  1. 我不喜欢 dom 树中已经有 <iframe id="ifmcontentstoprint" style="height: 0px; width: 0px; position: absolute"></iframe>
  2. 我想创建一种方法使其可重复使用。

我希望这能让其他人开心并节省几分钟的生命。现在去花额外的时间为某人做点好事。

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()
}

编辑 2019-7-23:在更多地使用它之后,它确实有一个缺点,即它不能完美地渲染反应组件。当样式是内联的时,这对我有用,但当由样式化组件或其他一些情况处理时,这对我不起作用。如果我想出万无一失的方法,我会更新。

Emil Ingerslev 提供的解决方案运行良好,但 CSS 未应用于输出。在这里我找到了Andrewlimaza给出的一个很好的解决方案。它打印给定 div 的内容,因为它使用 window 对象的打印方法,所以 CSS 不会丢失。而且也不需要额外的 iframe。

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

更新 1: 在 chrome/firefox/opera/edge 中存在异常行为,执行此代码后打印或其他按钮停止工作。

更新 2: 给出的解决方案在上面link的评论中:

.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>

完美解决方案:

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

只需转到 React 文件中的任何位置并执行以下操作:

import PrintThisComponent from 'the file having above code';

<PrintThisComponent />