Cách triển khai in từ trình duyệt với kiểm soát định dạng và tương thích đa nền tảng

Để tích hợp chức năng in trực tiếp từ giao diện web một cách ổn định trên các trình duyệt hiện đại, cần kết hợp nhiều kỹ thuật: tạo bản in động, điều chỉnh CSS dành riêng cho in, xử lý phân trang, và vượt qua giới hạn về bảo mật popup.

Tạo bản in động bằng DOM và iframe

Thay vì mở cửa sổ mới (window.open) — dễ bị chặn bởi trình duyệt — giải pháp được ưu tiên là sử dụng <iframe> ẩn để render nội dung in. Đoạn mã sau minh họa cách xây dựng bản in dựa trên các phần tử có class "print-section", đồng thời đảm bảo không bị chặn bởi cơ chế popup blocker:

function initiatePrint() {
  const sections = document.querySelectorAll('.print-section');
  let htmlContent = '';

  sections.forEach(section => {
    htmlContent += section.innerHTML;
  });

  const printDoc = `
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <style>
          @page {
            size: A4;
            margin: 0mm;
          }
          @media print {
            body {
              margin: 0;
              padding: 15mm;
              font-family: "Segoe UI", sans-serif;
            }
            .no-print { display: none !important; }
            .page-break { page-break-after: always; }
          }
        </style>
      </head>
      <body>
        ${htmlContent}
      </body>
    </html>
  `;

  const iframe = document.createElement('iframe');
  iframe.style.position = 'absolute';
  iframe.style.width = '0';
  iframe.style.height = '0';
  iframe.style.border = 'none';
  document.body.appendChild(iframe);

  const frameDoc = iframe.contentDocument || iframe.contentWindow?.document;
  frameDoc.open();
  frameDoc.write(printDoc);
  frameDoc.close();

  // Đợi DOM tải xong rồi in
  setTimeout(() => {
    iframe.contentWindow?.focus();
    iframe.contentWindow?.print();
    document.body.removeChild(iframe);
  }, 200);
}

Điều khiển định dạng in bằng CSS media query

Thuộc tính @page chỉ hoạt động trong ngữ cảnh @media print. Để loại bỏ tiêu đề/trang chân tự động của trình duyệt, thiết lập margin: 0mm là bắt buộc — tuy nhiên, cần lưu ý rằng một số trình duyệt (như Chrome) vẫn giữ khoảng cách tối thiểu nếu không dùng thêm thuộc tính sizemargin đồng bộ. Ví dụ cấu hình in chuẩn A4:

@media print {
  @page {
    size: 210mm 297mm; /* A4 */
    margin: 0;
  }
  body {
    margin: 12mm; /* Lề nội dung thực tế */
    line-height: 1.4;
  }
  table {
    width: 100%;
    border-collapse: collapse;
  }
}

Xử lý đơn vị đo lường phù hợp

Khi thiết kế layout in, tránh dùng px hoặc % — thay vào đó nên sử dụng đơn vị vật lý như mm, cm, hoặc in. Với độ phân giải in tiêu chuẩn (300 DPI), kích thước giấy A4 tương ứng chính xác là 210mm × 297mm. Việc quy đổi từ pixel sang mm chỉ mang tính tham khảo cho màn hình (96 DPI):
→ 1 cm ≈ 37.8 px
→ 100 px ≈ 2.65 cm

Phân trang chủ động

Để ngắt trang tại vị trí mong muốn, thêm lớp CSS .page-break vào phần tử cuối cùng của mỗi trang:

<div class="print-section">
  <h2>Báo cáo tháng 01</h2>
  <table>...</table>
  <div class="page-break"></div>
</div>
<div class="print-section">
  <h2>Báo cáo tháng 02</h2>
  <table>...</table>
</div>

Xử lý quyền in bất đồng bộ

Khi cần kiểm tra quyền trước khi in (ví dụ: gọi API xác thực), hãy thực hiện kiểm tra đồng bộ trước khi khởi tạo iframe. Nếu yêu cầu bất đồng bộ là bắt buộc, dùng Promise kết hợp async/await để đảm bảo luồng điều khiển rõ ràng:

async function safePrint() {
  try {
    const hasPermission = await checkPrintPermission(); // Hàm trả về Promise<boolean>
    if (!hasPermission) {
      alert('Bạn không có quyền in tài liệu này.');
      return;
    }
    initiatePrint();
  } catch (error) {
    console.error('Kiểm tra quyền in thất bại:', error);
  }
}

Thẻ: css-print web-printing iframe-printing page-break a4-layout

Đăng vào ngày 9 tháng 8 lúc 13:05