全流程教你 window.print

2,596 阅读3分钟

前言

前端实现打印方案有很多,但是有一些缺点与不足

主要缺点

  • 没有预览功能

  • 打印不清晰

  • 分页切分显示不全

  • ...

并且,信息填写类实现打印功能,一般要有 [预览] 和 [打印] 两个功能

很多插件都不支持 [预览] 功能,而且如果你只是实现简单的 [预览] 和 [打印],完全没必要折腾其他插件

实现

window.print

首先我们创建一个 index.html,修改下基本样式

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
  
    .list-card {
      width: 900px;
      background: #ffffff;
      border-radius: 10px;
      margin: 20px 0;
      padding: 20px;
    }
  
    .table-layout {
      width: 100%;
      table-layout: fixed;
      border-collapse: collapse;
    }
  
    .table-cell {
      display: table-cell;
      text-align: center;
      height: 60px;
      border: 1px solid #e4e4e4;
      color: #333333;
      font-size: 14px;
    }
  </style>
</head>
<body>
  <button onclick="print()">打印</button>
  <div class="list-card">
    <div class="table-container">
      <div class="table-container">
        <table class="table-layout">
          <tbody>
            <tr>
              <td class="table-cell">
                左青龙
              </td>
              <td class="table-cell" colspan="6">
                右白虎
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</body>
</html>

image-20240808182544459.png

效果有了,现在我们需要去做点击下打印按钮,就会调起浏览器打印

我们写个函数来实现

const print = () => {
    const newDom = document.querySelector(".list-card").innerHTML;
    const oldDom = document.body.innerHTML;
    document.body.innerHTML = newDom;
    window.print();
    document.body.innerHTML = oldDom;
}

image-20240808182838739.png

可以出现来效果了

但是,会有两个问题

  • 打印的表格右边显示不全
  • 我们再点击打印按钮,无效了

先解决下,打印不全的问题,我们可以通过 @Page 来设置打印的样式

@page {
  margin: 20px 20px 0;
}

image-20240808192559397.png

可以了,那这个打印按钮无效的问题呢?

那是因为,我们调用 window.print() 后,整个页面都被锁住了

我们需要通过 window.location.reload() 重新加载页面

但是这样就会,导致页面重新刷新,那么如果是需要填写的表单,可能数据就没了

为了解决这问题,我们可以使用 iframe 来实现

const print = () => {
  // 获取打印的元素
  const dom = document.querySelector(".list-card").outerHTML;
  // 创建 iframe,设置 absolute、width、height 让元素隐藏再浏览器窗口外,并将 iframe 加入到 body 中
  const iframe = document.createElement("iframe");
  iframe.setAttribute("style", "position:absolute;width:0;height:0;top:-10px;left:-10px;");
  document.body.appendChild(iframe);
   
  const iframeDom = iframe.contentDocument;

  let str = ''
  const styles = document.querySelectorAll("style,link").forEach(item => {
    str += item.outerHTML;
  });

  // 将打印元素与样式加入 iframe 
  iframeDom.open();
  iframeDom.write(str + dom);
  iframeDom.close();

  iframe.contentWindow.print();
  // 打印关闭后,移除 iframe 元素
  document.body.removeChild(iframe);
}

实现逻辑就是,将要打印的内容放到 iframe ,打印完成后移除 iframe

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
/*   
    body {
      background: #000000;
    } */
  
    .list-card {
      width: 900px;
      background: #ffffff;
      border-radius: 10px;
      margin: 20px 0;
      padding: 20px;
    }
  
    .table-layout {
      width: 100%;
      table-layout: fixed;
      border-collapse: collapse;
    }
  
    .table-cell {
      display: table-cell;
      text-align: center;
      height: 60px;
      border: 1px solid #e4e4e4;
      color: #333333;
      font-size: 14px;
    }
  
    @page {
      /* size: auto; */
      margin: 20px 20px 0;
    }
  </style>
</head>
<body>
  <button onclick="print()">打印</button>
  <div class="list-card">
    <div class="table-container">
      <div class="table-container">
        <table class="table-layout">
          <tbody>
            <tr>
              <td class="table-cell">
                左青龙
              </td>
              <td class="table-cell" colspan="6">
                右白虎
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</body>
</html>
<script>
  // const print = () => {
  //   const newDom = document.querySelector(".list-card").innerHTML;
  //   const oldDom = document.body.innerHTML;
  //   document.body.innerHTML = newDom;
  //   window.print();
  //   document.body.innerHTML = oldDom;
  //   window.location.reload();
  // }

const print = () => {
  const dom = document.querySelector(".list-card").outerHTML;
  const iframe = document.createElement("iframe");
  iframe.setAttribute("style", "position:absolute;width:0;height:0;top:-10px;left:-10px;");
  document.body.appendChild(iframe);

  const iframeDom = iframe.contentDocument;

  let str = ''
  const styles = document.querySelectorAll("style,link").forEach(item => {
    str += item.outerHTML;
  });

  iframeDom.open();
  iframeDom.write(str + dom);
  iframeDom.close();

  iframe.contentWindow.print();
  document.body.removeChild(iframe);
}

</script>

小结

window.print 直接实现打印,会出现引起页面刷新,从而容易导致页面已有表单信息缺失

为了解决这个问题,我们将打印数据放入 iframe ,再调用浏览器打印,完成后再删除 iframe

其中需要注意的是,iframe 得让其远离浏览器视图,这样才能保证不会影响到原来的视图