Reputation: 1692
Printing the contents of an iFrame already seemed a challenging problem to solve cross-browser. After testing a lot of approaches (some of which also found on this site), my current approach seems to work quite good cross-browser and looks like this:
function printUrl( elem, url ) {
$( '#' + elem ).append( "<iframe style='border: none; width: 0; height: 0; margin: 0; padding: 0;' src='" + url + "' id='printFrame'></iframe>" );
$( '#printFrame' ).load( function() {
var w = ( this.contentWindow || this.contentDocument.defaultView );
w.focus();
w.print();
} );
}
There is only a slight problem with this code when using an iPad. The iPad prints the page which contains the iFrame, instead of the contents of the iFrame. Safari on Mac correctly prints the contents of the iFrame, though.
Has anyone already solved this problem and been able to print the contents of an iFrame on an iPad?
Upvotes: 5
Views: 4644
Reputation: 1583
I used @d13 answer with a little bit modification. because window.frames["frame1"]
is not a valid object. window.frames is an array like object.
Returns the window itself, which is an array-like object, listing the direct sub-frames of the current window...
Each item in the window.frames pseudo-array represents the Window object corresponding to the given 's or 's content, not the frame or iframe DOM element (i.e., window.frames[0] is the same thing as document.getElementsByTagName("iframe")[0].contentWindow).
So I have used below method:
export function printHtml(html: string) {
// Create an iframe element
const iframe = document.createElement('iframe');
iframe.style.position = 'absolute';
iframe.style.width = '0';
iframe.style.height = '0';
iframe.style.border = 'none';
// Append the iframe to the body
document.body.appendChild(iframe);
// Write the content to the iframe
const doc = iframe.contentWindow?.document;
if (doc) {
doc.open();
doc.write(html);
doc.close();
}
// we need to use the print this way to be able to print in phones
setTimeout(() => {
window[0].focus();
window[0].print();
setTimeout(() => {
document.body.removeChild(iframe); // Clean up by removing
}, 500);
}, 500);
}
Upvotes: 0
Reputation: 10077
Here's a function that works cross-cross platform on evergreen browsers and the current versions of iOS:
function printElement(divid, title)
{
var contents = document.getElementById(divid).innerHTML;
var frame1 = document.createElement('iframe');
frame1.name = "frame1";
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><title>' + title + '</title>');
frameDoc.document.write('</head><body style="font-family: Arial, Helvetica, sans; font-size: 14px; line-height: 20px">');
frameDoc.document.write('<h1>' + title + '</h1>');
frameDoc.document.write(contents);
frameDoc.document.write('</body></html>');
frameDoc.document.close();
setTimeout(function () {
window.frames["frame1"].focus();
window.frames["frame1"].print();
}, 500);
//Remove the iframe after a delay of 1.5 seconds
//(the delay is required for this to work on iPads)
setTimeout(function () {
document.body.removeChild(frame1);
}, 1500);
return false;
}
This is based on this answer with minor modifications (Importantly the line document.body.removeChild(frame1);
had to be removed to allow for printing on iOS.)
Upvotes: 1
Reputation: 1692
Okay, first of all. I did not solve the problem. I created a work-around that actually fakes what I want to achieve.
Because the iPad / iPhone simple prints the parent page, I wrap the complete body in a new div, then append the iFrame and some stylesheets which make sure that the printed document only contains the iFrame:
function printUrl( url ) {
$newBody = "<div class='do_not_print_this'>"
+ $( 'body' ).html()
+ "</div>"
+ "<iframe style='border: none; 0; width: 100%; margin: 0; padding: 0;' src='" + url + "' class='printFrame'></iframe>"
+ "<style type='text/css' media='all'>.printFrame { position: absolute; top: -9999999px; left: -99999999px; }</style>"
+ "<style type='text/css' media='print'>.do_not_print_this { display: none; } .printFrame { top: 0; left: 0; }</style>";
$( 'body' ).html( $newBody );
$( '.printFrame' ).load( function() {
var w = ( this.contentWindow || this.contentDocument.defaultView );
w.focus();
w.print();
} );
}
Hiding the iframe for the browser in the normal view is done using absolute positioning, using display on none or visibility hidden introduced weird behavior in the final print.
Yes, it's ugly. However, this is currently the only option I can think of which works. If any of you come up with a better solution, please let me know.
Upvotes: 1