The print function only works after a second click

I have this function for printing div.
Whenever the page loads, and I click on the "Print" link that I have, the DIV as shown is printed without CSS.
If I close the Chrome print render page and click the Print link again, the DIV uses CSS.

Any ideas why?

Javascript

function printDiv(divId) { var printDivCSSpre = '<link href="/static/assets/vendor/sb-admin-2-1.0.7/bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">' + '<link href="/static/assets/vendor/sb-admin-2-1.0.7/dist/css/sb-admin-2.css" rel="stylesheet">' + '<div style="width:1000px; padding-right:20px;">'; var printDivCSSpost = '</div>'; $('body').append('<iframe id="print_frame" name="print_frame" width="0" height="0" frameborder="0" src="about:blank"></iframe>'); $("link").clone().appendTo($("#print_frame").contents().find("head")); window.frames["print_frame"].document.body.innerHTML = printDivCSSpre + document.getElementById(divId).innerHTML + printDivCSSpost; window.frames["print_frame"].window.focus(); var windowInstance = window.frames["print_frame"].window; windowInstance.print(); } 

HTML

 <a id="print" href="#"> <i class="fa fa-print"></i> Print </a> <script> $('#print').click(function () { printDiv('report') }) </script> <div id="report" class="report"> <p># Generated Table#</p> </div> 

First click:

http://imgur.com/a/Go81Y

Close the print preview page and press the print button again.

http://imgur.com/a/SCxJF

+6
source share
6 answers

That's right, just change the sequence. In the browser debugger, on the first click, it did not show "print_frame" in the sources section, but on the second click (I use chrome devtool).

So load into a memory frame with css attributes at boot time:

 var windowInstance; $(function(){ $('body').append('<iframe id="print_frame" name="print_frame" width="0" height="0" frameborder="0" src="about:blank"></iframe>'); $("link").clone().appendTo($("#print_frame").contents().find("head")); windowInstance = window.frames["print_frame"].window; }); 

and onClick just add html

 $('#print').click(function () { var divId = 'report'; var printDivCSSpre ='<div id="printReportDiv" style="width:1000px; padding-right:20px;">'; var printDivCSSpost = '</div>'; window.frames["print_frame"].document.body.innerHTML = printDivCSSpre + document.getElementById(divId).innerHTML + printDivCSSpost; window.frames["print_frame"].window.focus(); windowInstance.print(); }); 

updated jsfiddle

+1
source

This is because when you call the printDiv () function, css is also written using internal HTML and in this script, CSS is not applied the first time you click, because you wrote CSS to elements, even if they do not exist inside the DIV.

A function to work as desired should first write the contents of the DIV, and then apply CSS. I would say write css after the contents of the DIV or load over your HTML page and just write the contents of the DIV.

Hope this helps.

+3
source

As other people have said, it's hard to understand your problem without seeing a working example of the problem, but just guessing the code:

  • The browser cannot load CSS before calling print() .
  • The browser cannot display CSS before calling print() .

Keeping in mind changing your JS function this way you can do the trick

 function printDiv(divId) { $("link").clone().appendTo($("#print_frame").contents().find("head")); window.frames["print_frame"].document.body.innerHTML = printDivCSSpre + document.getElementById(divId).innerHTML + printDivCSSpost; window.frames["print_frame"].window.focus(); var windowInstance = window.frames["print_frame"].window; setTimeout(function() { windowInstance.print(); }, 0); } 

The idea behind this function is to let the browser execute its code after adding changes to the HTML / CSS code in the window - see Why is setTimeout (fn, 0) used sometimes?

WARNING: this approach is not tested for your specific problem, and it may also not work, because we avoid / leave the stack of mouse click calls, calling the print() method may not be possible from the user interaction stack.

UPDATE: after viewing in jsfiddle - my assumption was correct, the browser needs some time to load and display CSS, so calling print() immediately after changing the contents of the iframe does not give the desired result. There are 3.5 ways to solve this:

  • Use events to determine when the iframe and window have finished loading and rendering. I tried two approaches and still could not read them, I need to read the documents more carefully when the documents and the window behave during the loading sequence:
    • we can do this from outside the iframe, that is, listen to the events of the iframe element and its children
    • we can do this from within an iframe, i.e. add a small javascript fragment inside which a message will be sent to the parent window at boot time.
  • Think about how to shape the print result differently, how about sheets of print styles? That is, to add another stylesheet with a print request to the parent document and just invoke printing on it?
  • Consider forming an iframe that is already loaded and ready to print, but replace only the contents of the table inside it.
+2
source

Try it. The problem arises mainly because css was not applied to the page when the print command was run. setTimeout is one way to solve this problem, as others have mentioned, but it is actually impossible to predict how much delay you will need. Slow Internet connections will take a long time before you start printing. However, the following code fires a print event only after css has been applied correctly in the iframe.

 $('#print').click(function () { if($("#print_frame").length == 0) { $('#report').after('<iframe id="print_frame" name="print_frame" width="0" height="0" frameborder="0" src="about:blank"></iframe>'); } var $head = $("#print_frame").contents().find("head"); // for now for ease I will just empty head // ideally you would want to check if this is not empty // append css only if empty $head.empty(); $.ajax({ url : "https://dl.dropboxusercontent.com/u/7760475/reports.css", dataType: "text", success : function (reports) { // grab css and apply its content to the iframe document $head.append('<style>'+reports+'</style>'); $.ajax({ url : "https://dl.dropboxusercontent.com/u/7760475/bootstrap.css", dataType: "text", success : function (bootstrap) { // grab another css and apply its content to the iframe document // there may be better ways to load both css files at once but this works fine too $head.append('<style>'+bootstrap+'</style>'); // css has been applied // clone your div and print var $body = $("#print_frame").contents().find('body'); // empty for ease // but later append content only if empty $body.empty(); $("#report").clone().appendTo($body); $('#print_frame').get(0).contentWindow.print(); } }); } }); }); 
+2
source

Use inline CSS instead. Reason: when we print or save as PDF, if we cannot get external css files, therefore we should use Inline css. edited your file see: jsfiddle.net/ytzcwykz/18/

+1
source

As mentioned above, the problem is that the CSS files used are external resources, and the browser needs time to load and cache locally. Once it is cached, it will work faster, and therefore it works differently from the second click.

As Anton said, setTimeout is the key! You can probably increase the timeouts to make this work. I tried to set it to 500 ms and it worked,

 setTimeout(function(){windowInstance.print();},500); 
0
source

Source: https://habr.com/ru/post/1014734/


All Articles