Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

safari not allowing a second window.print()

Hello I am running into a weird issue in Safari. I have a button and when clicked it prints the content of the html. My issue is that when calling on window.print() the first time it works great, however, on the second click it will display a popup stating:

'This webpage is trying to print. Do you want to print this webpage?'

If I click Print in this dialog nothing happens. Any ideas why this could be happening? Thank you in advance!

Javascript -

$scope.print = function() {
    var contents = document.getElementById("print-section").outerHTML;
    var frame1 = document.createElement('iframe');
    frame1.name = "frame3";
    frame1.style.position = "absolute";
    frame1.style.top = "-1000000px";
    document.body.appendChild(frame1);

    var frameDoc = frame1.contentWindow ? frame1.contentWindow : frame1.contentDocument.document ? frame1.contentDocument.document : frame1.contentDocument;
    frameDoc.document.open();
    frameDoc.document.write('<html><head>'); // add some libraries for the new document
    frameDoc.document.write('</head><body>');
    frameDoc.document.write(contents);
    frameDoc.document.write('</body></html>');
    frameDoc.document.close();
    setTimeout(function () {
        window.frames["frame3"].focus();
        window.frames["frame3"].print();
        document.body.removeChild(frame1);
    }, 500);

    return false;
};

Html-

<div id="print-section">
   <div>Section to print<>
</div>
like image 647
paul590 Avatar asked Jan 30 '18 15:01

paul590


People also ask

How do I enable Print on Safari?

In the Safari app on your Mac, choose File > Print. Click the options pop-up menu (in the separator bar), choose Safari, then set the webpage printing options. If you don't see the options pop-up menu in a separator bar to the right of the page preview, click Show Details at the bottom of the Print dialog.

What is Print JS?

Print. js was primarily written to help us print PDF files directly within our apps, without leaving the interface, and no use of embeds. For unique situations where there is no need for users to open or download the PDF files, and instead, they just need to print them.


2 Answers

As Matteo Conta correctly mentioned, the problem is that Safari's print confirmation dialog does not stop JS code execution flow to wait for printing.
Therefore, what we really want is to detect when exactly does a print action end in order to run a cleanup absolutely safely.

setTimeout solution is a good starting point but I wanted to tackle the problem more reliably and have come up with an event-based solution using matchMedia and onfocus to catch the exact moment when printing is finished (either cancelled or completed).

Below is the code that I've tested for this particular question (ES5, iframe printing).
Also, I've created a Gist on GitHub with a generic approach demonstrated. Hope it will be useful.

$scope.print = function () {
  var frame = appendFrame();

  // Safari
  if (!window.onafterprint) {
    // emulate onbeforeprint/onafterprint events
    var mediaQueryCallback = function (mql) {
      if (!mql.matches && frame) {
        document.body.removeChild(frame);
      }
    };
    var mediaQueryList = window.frames[frame.name].matchMedia('print');
    mediaQueryList.addListener(mediaQueryCallback);

    // the code below will trigger a cleanup in case a user hits Cancel button
    // in that Safari's new additional print confirmation dialog
    window.frames[frame.name].focus();
    window.frames[frame.name].onfocus = function () {
      return mediaQueryCallback(mediaQueryList);
    };
  }

  window.frames[frame.name].print();

  return false;
};

For brevity I've extracted the frame creation code into a trivial function (appendFrame) and omitted setTimeout call from the original question (after frameDoc.document.close(); line).

var appendFrame = function () {
  var contents = document.getElementById("print-section").outerHTML;
  var frame1 = document.createElement('iframe');
  frame1.name = "frame3";
  frame1.style.position = "absolute";
  frame1.style.top = "-1000000px";
  document.body.appendChild(frame1);

  var frameDoc = frame1.contentWindow ? frame1.contentWindow : frame1.contentDocument.document ? frame1.contentDocument.document : frame1.contentDocument;
  frameDoc.document.open();
  frameDoc.document.write('<html><head>'); // add some libraries for the new document
  frameDoc.document.write('</head><body>');
  frameDoc.document.write(contents);
  frameDoc.document.write('</body></html>');
  frameDoc.document.close();

  return frame1;
};

inspired by MDN article:

onbeforeprint

like image 179
Fedor Avatar answered Sep 23 '22 14:09

Fedor


After some investigation I found a solution.

Safari shows the print warning only if you "Cancel" the print. However the reason for the second blank print is that you remove too quickly the content using

document.body.removeChild(frame1)

If you increase the waiting time from 500ms to 5 secs you give the user the time to close the warning popup and print.

In my case I succesfully tested on Chrome/FF/Edge/Safari this jQuery plugin, increasing also in this library the removal timeout https://github.com/DoersGuild/jQuery.print

like image 27
Matteo Conta Avatar answered Sep 23 '22 14:09

Matteo Conta