返回

PHP打印页码问题终极解决:CSS与JS方案详解

javascript

PHP 打印页面编号问题:添加细节及解决方案

这个问题被标记为需要更多细节或清晰度。 根本问题在于使用 PHP 和 JavaScript 尝试在打印的每一页上添加页码,但只有第一页显示了正确的页码("Page 1 of 2"),后续页面没有正确显示。

问题原因分析

  1. JavaScript 的 beforeprint 事件时机: beforeprint 事件在打印对话框出现 之前 触发。此时,由 PHP 产生的 <div class="page-break"> 元素已经被解析, 并且根据这个去插入页码。
  2. 页码插入位置问题: 代码中将页码元素插入到了 .page-break 元素的父元素之后,这可能是造成页面只显示一次的问题,因为footer content是Fixed定位的。
  3. page-number1 类的干扰: 已经定义 page-number 处理页码。 页脚代码内, 有重复类 page-number1, 干扰正常运作。
  4. 单页判断只将元素加入到了document.body, 也是无法正确定位, 会跑到页面左上角.

解决方案

方案一:纯 CSS 解决方案(推荐)

最简单也最可靠的方法是利用 CSS 的 counter@page 规则。这种方法不需要 JavaScript,浏览器会自动处理分页和页码计数。

  1. 原理:

    • @page:允许为特定页面类型定义样式。
    • counter-increment:递增计数器。
    • counter-reset: 重设计数器。
    • content: counter(page):在伪元素中显示计数器的值。
  2. 代码示例:

    @media print {
      @page {
        /* 在底部中心添加页码 */
        @bottom-center {
          content: "Page " counter(page) " of " counter(pages);
        }
      }
    
          /*确保所有div不会影响分页, 使强制分页生效*/
        body > div, body > table, .print-breaks {
            page-break-inside: avoid;
        }
    
        .page-break{
            page-break-before: always;
        }
    
      /*重写原本的page number*/
       .page-number{
           display:none;
        }
    
      /*如果有原本page-number1 class的footer, 需要调整bottom, 不可与@bottom-center 重叠*/
        #printFooter {
          bottom:40px; /* 避免遮挡 */
        }
    }
    
  3. PHP 代码 (无需修改PHP插入<div class="page-break">的地方):

    <?php
    // Check the number of stakeholders and activities
    $stakeholders_count = count($stakeholder_percentage);
    $activities_count = count($activity_name_en);
    
    if ($stakeholders_count >= 5 || $activities_count >= 10) {
    // Apply page break when the condition is met
      echo '<div class="page-break" style="page-break-before: always;"></div>';
      echo '<div class="print-breaks">
        <br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
    </div>';
    } else {
        echo '<div>';
    }
    ?>
    
  4. 删除 page-number1

  5. 解释和作用:

    • @page 规则应用于打印的每个页面。
    • @bottom-center 定义了位于页面底部中心区域的样式。
    • content 属性使用 counter(page) 获取当前页码,并使用 counter(pages) 获取总页数(仅当浏览器支持时才显示总页数;某些浏览器可能不支持 pages 计数器,此时只显示当前页码)。
  6. 额外建议:

    • 这种方法通常是最可靠的,因为它完全依赖于浏览器的内置打印功能。
    • 你可以调整 @bottom-center 内的样式(如 font-sizemargin)来控制页码的外观和位置。
    • 确保底部footer 和 CSS 页码位置不重合。

方案二:改进 JavaScript 解决方案

如果你一定要用 JavaScript,可以修改代码以获得更好的结果,不过不如纯CSS稳定,特别是在比较复杂的文档布局中。

  1. 原理:

    • beforeprint 事件中计算总页数不够可靠,因为此时文档的最终布局可能尚未完全确定(尤其是在有图片、表格等异步加载内容时)。
    • 最好是在页面加载完成后 (DOMContentLoadedload 事件) 立即计算页数和插入页码元素,并利用 CSSOM 处理分页。
  2. 代码示例:

    function insertPageNumbers() {
      const pageBreaks = document.querySelectorAll('.page-break');
      const totalPages = pageBreaks.length + 1;
      const footers = document.querySelectorAll("#printFooter");
    
    
         // 如果有且只有一个页脚, 且只有一页
         if(totalPages === 1 && footers.length ===1 ){
                const pageNumberElement = document.createElement("div");
                pageNumberElement.classList.add("page-number");
                pageNumberElement.textContent = "Page 1 of 1";
               footers[0].appendChild(pageNumberElement);
                 //移除原本的.page-number1 避免显示两个
                 const existNumber1=  footers[0].querySelector(".page-number1");
                 if(existNumber1){
                       existNumber1.remove();
                  }
    
               return;
        }
    
      //遍历footer加入page number.
       footers.forEach(function(footer,index){
               const pageNumberElement = document.createElement("div");
                pageNumberElement.classList.add("page-number");
                pageNumberElement.textContent = "Page " + (index + 1) + " of " + totalPages;
              footer.appendChild(pageNumberElement);
    
             //移除原本的.page-number1 避免显示两个
             const existNumber1=  footer.querySelector(".page-number1");
             if(existNumber1){
                   existNumber1.remove();
              }
    
        });
    }
    // 使用 DOMContentLoaded 保证页面主要内容解析完成就可执行
     document.addEventListener('DOMContentLoaded', insertPageNumbers);
    
    
  3. HTML/PHP 改动

  • 给每一个 footer 增加 id。 或者使用其他更准确的方式查询到每一个 Footer 元素。
```html
  <div style="position: fixed; bottom: 20px; left: 0; width: 100%; text-align: center;" id="printFooter">
      <!-- .... Footer Content ..... -->
</div>
```
  1. CSS:

    @media print {
      /* Ensuring page numbers appear on each page */
      .page-number {
          position: absolute; /*绝对定位,相对于footer*/
          bottom: 10px;
          right: 10px;
          font-size: 12px;
      }
    
         /*确保所有div不会影响分页, 使强制分页生效*/
        body > div, body > table, .print-breaks{
            page-break-inside: avoid;
        }
    
        .page-break{
            page-break-before: always;
        }
     }
    
  2. 删除原本的 page-number1

  3. ** 解释:**

    • 通过 DOMContentLoaded 事件确保大部分 dom 元素已被解析.
    • page-number 使用绝对定位。 插入到 footer 内部。
    • 避免 beforeprint 可能的副作用。
  4. ** 局限性:**
    这种方法依然有可能在非常复杂的页面,或者浏览器对一些特性实现有差异的页面失效。 纯CSS方案更健壮。

总结

纯 CSS 方案(方案一)是最简单且兼容性最好的方法,强烈推荐。 如果坚持使用JavaScript, 则采用方案二可以修正你目前存在的问题, 但是复杂布局下依然不保证100%成功,需要测试调整。

最终建议,选择方案一 (纯 CSS)来处理打印页码,这是最简洁、最可靠的方案,且符合 Web 标准。