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>
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>
Share
Improve this question
edited Mar 11, 2019 at 20:43
Fedor
1,5783 gold badges29 silver badges39 bronze badges
asked Jan 30, 2018 at 15:14
paul590paul590
1,4351 gold badge25 silver badges45 bronze badges
1
- 1 Same problem here, any ideas? – Matteo Conta Commented Mar 14, 2018 at 15:06
3 Answers
Reset to default 5As 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 e up with an event-based solution using matchMedia
and onfocus
to catch the exact moment when printing is finished (either cancelled or pleted).
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
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./DoersGuild/jQuery.print
Adding onto this, using the jQuery Print plugin Matteo Conta mentions above, I was able to implement printing in Safari by doing a print in a new tab, no iframes, popup dialogue, blank prints, or delays.
The issue I was running into, as many people are and have been, was Safari having issues with subsequent prints within iframes, showing that print dialogue, and then a blank print.
Currently we use printThis JS as our printing plugin and had already implemented a 3000ms delay, but this new update Big Sur 14.1 specifically broke that implementation. jQuery Print plugin does not require any delays and may deprecate our use of printThis if we no longer e across anymore issues with Safari updates.
Note: Even with jQuery Print, if you choose to still print within an iframe you'll e across the same printing issues.
Below is a snippet of how I implemented the print based upon the user's browser. Hope this helps anyone still searching for a solution!
var currentBrowser;
// Determine user browser
if ( navigator.userAgent.indexOf("Chrome") != -1 ) {
currentBrowser = "Google Chrome";
} else if ( navigator.userAgent.indexOf("Safari") != -1 ) {
currentBrowser = "Safari";
} else {
currentBrowser = "Others";
}
// Special Safari Treatment - Print New Window
if( currentBrowser === "Safari" ) {
/**
* jQuery Print JS
*
* Print receipts in new window with a deffered callback function.
*
* globalStyles - Will include style sheets from parent document
* iframe - Print in a new window, iframe = false
* deferred - Callback function
*/
$("#printDiv").print({
globalStyles : true,
iframe : false,
deferred: $.Deferred().done(function() {
// Callback
})
});
} else {
// Print iframe normally using printThis JS
$('#printDiv').printThis();
}