【问题标题】:How to print React component on click of a button?如何在单击按钮时打印 React 组件?
【发布时间】:2015-05-09 02:45:01
【问题描述】:

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

我知道这个解决方案:

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

但是 React 不想使用框架。

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

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    客户端上有两种解决方案。一个是像你发布的框架。您可以使用 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 进行设置。

    【讨论】:

    • 我检查它是因为我现在正在使用它。但这不是一个好的解决方案,因为它会在大多数浏览器(Firefox 和 Chrome)中被阻止,因为它的行为就像一个弹出窗口。
    • 我不确定我是否理解。我提到的所有解决方案都不会触发弹出窗口阻止程序。使用 CSS,不会,使用现有的 iframe 不会,使用服务器方法肯定不会。你能解释一下你的意思吗?
    • 哪些解决方案会触发此问题?
    • 使用 window.open 肯定会触发弹出窗口拦截器,但使用上例中的 iframe 并且不应该打开文档。
    • 它就像一个魅力,感谢@Emil Ingerslev 的解决方案。我正在使用第一个解决方案,唯一的问题是前端可见一个像素点。
    【解决方案2】:

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

    你可以这样使用它:

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

    【讨论】:

    • @Gregory Nowakowski - 这个库可以处理 css 模块传递的 css 吗?如果有,怎么做?
    • @Gregory Nowakowski,我想通过单击打印来自多个组件的多个收据。有用吗?
    • @MuthukumarMarichamy 你可以在给定的引用中提供任何内容
      (this.componentRef = el)}>
      跨度>
    • 我正在尝试使用它。但是点击什么都没有发生
    • @VinitKhandelwal 您使用的是引导程序还是其他 css 库?如果是这样,请确保您阅读了如何使用 ref 的文档。一些使用 innerRef 而不是 ref 的库
    【解决方案3】:

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

    export default class Component extends Component {
    
        print(){
            window.print();
        }
    
    
      render() {
    
      ...
      <span className="print"
                  onClick={this.print}>
        PRINT
        </span>
    
      } 
    }
    

    希望对你有帮助!

    【讨论】:

    • 您的代码在 Chrome 和 Firefox 中运行良好。但是,在我的情况下,在 Safari 中是不是什么都不做?你知道为什么吗?我在这里看到代码是相同的,它可以在 Safari 中运行...w3schools.com/jsref/met_win_print.asp
    • 嗨 @AralRoca 我不确定,但 Safari 可能有点奇怪,找到了这个,我可能会尝试:stackoverflow.com/questions/44925902/…
    【解决方案4】:

    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
    

    【讨论】:

    • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
    • 这对这个问题不起作用 - 您提出的解决方案会打印整个页面,而不是按要求打印单个组件。
    • 它打印整个页面而不是一个组件。
    【解决方案5】:

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

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

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

    此 ID 应添加到实际 DOM 树的较高位置,以排除下面的“打印装载”以外的所有内容。

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

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

    【讨论】:

    • 这应该可以证明。当您按 Ctrl + P 打印时,只需观察结果窗口。 jsfiddle.net/captray/69z2wepo/68966
    • 如果你还需要document.getElementById('root')怎么办?
    • @FiddleFreak 不确定我是否理解您的问题?我发布这篇文章已经三年了,所以我可能需要更多背景信息。
    • 我的问题是没有反应,所以你只能有一个document.getElementById(),因为它被认为是一个单页应用程序?如果你需要像ReactDOM.render(&lt;MyTemplate/&gt;, document.getElementById('print-mount'), document.getElementById('root')) 这样的东西怎么办?
    • 仍然不能完全确定你想要做什么,但你的标记中可以有多个挂载点。此代码也已过时,但您可能希望查看源代码并了解它的作用,因为我不确定这是否满足您的需求。
    【解决方案6】:

    Emil Ingerslev 提供的解决方案工作正常,但 CSS 未应用于输出。在这里,我找到了Andrewlimaza 给出的一个很好的解决方案。它打印给定 div 的内容,因为它使用 window 对象的 print 方法,所以 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在cmets中有给出的解决方案:

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

    【讨论】:

    • 到目前为止,这是对我有用的一个。但是我看到的问题是当我单击取消并再次单击打印按钮时......什么都没有打开。
    【解决方案7】:

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

    1. 我不喜欢 &lt;iframe id="ifmcontentstoprint" style="height: 0px; width: 0px; position: absolute"&gt;&lt;/iframe&gt; 已经在 dom 树中。
    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:在更多地使用它之后,它确实有一个缺点,它不能完美地渲染反应组件。当样式是内联时,这对我有用,但在样式组件或其他一些情况下处理时不起作用。如果我想出一个万无一失的方法,我会更新。

    【讨论】:

      【解决方案8】:

      只是分享对我有用的东西,因为其他人可能会觉得它有用。我有一个模态,只是想打印模态的正文,它可能是纸上的几页。

      我尝试过的其他解决方案只打印了一页,并且只打印了屏幕上的内容。埃米尔接受的解决方案对我有用:

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

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

      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 中。

      【讨论】:

      • 您可以收集所有样式标签并像这样注入它们: const styleTags = Array.from(document.getElementsByTagName("style")).map(x => x.outerHTML).join (""); pri.document.write(styleTags);
      【解决方案9】:

      完美解决方案:

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

      【讨论】:

      • 感谢您的回答,但它或多或少与 sara-inés-calderón 和 Clay_F 的解决方案相同。除此之外,您的代码不能被复制粘贴,因为它在错误的位置包含一个分号,并且该指令不应该是代码的一部分。
      猜你喜欢
      • 2017-11-05
      • 1970-01-01
      • 1970-01-01
      • 2019-01-16
      • 2018-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多