简体   繁体   English

打印功能仅在第二次单击后有效

[英]Print function only works after second click

I have this function to print a DIV. 我有这个功能来打印DIV。
Whenever the page is loaded and I click in a "Print" link I have, the DIV is shown to be printed without CSS. 每当页面加载并点击我的“打印”链接时,显示DIV没有CSS打印。
If I close Chrome's print visualization page and click in the "Print" link again, the DIV has CSS applied. 如果我关闭Chrome的打印可视化页面并再次单击“打印”链接,则DIV已应用CSS。

Any ideas why? 有什么想法吗?

Javascript 使用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 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 http://imgur.com/a/Go81Y

Closing the print preview page and clicking again in print 关闭打印预览页面并再次单击打印

http://imgur.com/a/SCxJF http://imgur.com/a/SCxJF

This happens because when you call your printDiv() function, css is also written using inner HTML and in this scenario CSS is not applied during first click because you wrote CSS to the elements even when they do not exist inside DIV. 发生这种情况是因为当您调用printDiv()函数时,css也是使用内部HTML编写的,在这种情况下,CSS在第一次单击时不会应用,因为即使在DIV中不存在CSS,也会向元素编写CSS。

The function to work as desired has to write DIV contents first and then CSS should be applied. 根据需要工作的函数必须首先编写DIV内容,然后应该应用CSS。 I would say write css after contents of DIV or load on top of your HTML page and just write DIV contents. 我会说在DIV的内容之后写css或者在HTML页面上加载并且只写DIV内容。

Hope that helps. 希望有所帮助。

As other people mentioned it is hard to see your problem without seeing the working example of a problem, but just guessing from the code: 正如其他人提到的,如果没有看到问题的工作示例很难看到你的问题,而只是从代码中猜测:

  1. Browser is not able to load the CSS before your print() call. 浏览器无法在print()调用之前加载CSS。
  2. Browser is not able to render the CSS before your print() call. 浏览器无法在print()调用之前呈现CSS。

Keeping that in mind changing your JS function that way might do the trick 记住改变你的JS功能可能会有所帮助

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 browser execute it's code after we added changed the HTML/CSS code in the window - see Why is setTimeout(fn, 0) sometimes useful? 这个函数背后的想法是让我们在窗口中添加更改HTML / CSS代码后让浏览器执行它的代码 - 请参阅为什么setTimeout(fn,0)有时会有用?

WARNING: this approach is not tested for your particular problem, and it might also not work because we escape/leave the mouse-click call-stack, calling print() method might be not possible out of user-interaction stack. 警告:此方法未针对您的特定问题进行测试,并且它可能也不起作用,因为我们转义/离开鼠标单击调用堆栈,可能无法在用户交互堆栈之外调用print()方法。

UPDATE: after looking in the posted jsfiddle - my assumption was correct, the browser needs some time to load and render the CSS, that is why calling the print() right after changing iframe contents doesn't give the desired result. 更新:在查看发布的jsfiddle后 - 我的假设是正确的,浏览器需要一些时间来加载和渲染CSS,这就是为什么在更改iframe内容之后立即调用print()不会产生所需的结果。 There are 3.5 ways to solve that: 有3.5种方法可以解决这个问题:

  1. Use events to identify when iframe's document and window has finished loading and rendering. 使用事件来标识iframe文档和窗口何时完成加载和渲染。 I tried two approaches, and failed so far, need to read docs more carefully about when document and window are behiving during the loading sequence: 我尝试了两种方法,到目前为止都失败了,需要更仔细地阅读文档,了解文档和窗口在加载过程中的时间:
    • we can do that from outside of iframe, ie listen to events of iframe element and it's children 我们可以从iframe外部做到这一点,即听iframe元素的事件和它的孩子
    • we can do that from inside of iframe, ie add little javascript snippet inside which will send a message to the parent window when loading is done. 我们可以从iframe内部做到这一点,即添加一些小的javascript片段,里面会在加载完成后向父窗口发送一条消息。
  2. Consider forming the print result different, how about print style-sheets? 考虑将打印结果形成不同,打印样式表怎么样? Ie add one more style sheet with print-media query to the parent doc and just call print on it? 即添加一个带有打印媒体查询的样式表到父文档,并在其上调用print?
  3. Consider forming an iframe which is already loaded and ready to be printed, but replace just the table contents inside it. 考虑形成一个已加载并准备打印的iframe,但只替换其中的表内容。

Try this one. 试试这个吧。 The problem mainly arises because the css has not been applied to the page when the print command is initiated. 问题主要是因为在启动打印命令时css尚未应用于页面。 setTimeout is one way to solve it as others have mentioned but it is really not possible to predict how much delay you will need. setTimeout是解决它的一种方法,正如其他人提到的那样,但实际上无法预测你需要多少延迟。 Slow internet connections will require high delays before you fire the print statement. 在触发print语句之前,缓慢的Internet连接需要很长的延迟。 The following code, however, only fires the print event after the css has been properly applied to the iframe. 但是,以下代码仅在将css正确应用于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();
                    }
                });
            }
        });
});

As others mentioned, The problem here is that the CSS files used are external resources and browser takes time to download and cache it locally. 正如其他人提到的,这里的问题是使用的CSS文件是外部资源,浏览器需要时间下载并在本地缓存它。 Once it is cached, it would serve faster and that's why it works fine from the second click. 一旦它被缓存,它将更快地服务,这就是为什么它从第二次点击工作正常。

As Anton mentioned, setTimeout is the key here! 正如Anton所说,setTimeout是关键所在! You may probably increase the timeout seconds to make that work. 您可能会增加超时秒数以使其工作。 I tried setting it to 500ms and that worked, 我尝试将它设置为500毫秒,这是有效的,

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

Use inline CSS instead. 请改用内联CSS。 Reason: When we PRINT or save as PDF if fails to fetch external css Files, So we have to use Inline css. 原因:如果我们打印或保存为PDF,如果无法获取外部css文件,那么我们必须使用内联css。 edited your file please see: jsfiddle.net/ytzcwykz/18/ 编辑您的文件请参阅:jsfiddle.net/ytzcwykz/18/

Every thing is right just change the sequence. 每件事都是对的,只需改变顺序即可。 In browser debugger on first click it didn't show 'print_frame' in sources section while in second click it does (I am using chrome devtool). 在第一次单击时,它在浏览器调试器中没有在sources部分显示'print_frame',而在第二次单击时它(我使用chrome devtool)。

So load in memory frame with css attributes during onload: 因此在onload期间使用css属性加载到内存框架中:

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 append html 和onClick只是附加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 更新了jsfiddle

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM