0

I found the following code from a former colleague who is not in our company any more:

    var w = window.open('', 'print_window', 'width = 1000, height = 1000');
    w.document.write('<html><head><title>Printout</title>' + '<link rel="stylesheet" href="' + document.styleSheets[0].href + '"></head><body>');
    w.document.write(modal.dom.root.innerHTML);
    w.document.write('</body></html>');

    setTimeout(function() {
        $(w.document.body).find('.modal-print').remove();
        w.document.close();
        w.focus();
        w.print();
        w.close();
    }, 500);

I want to reduce the amount of setTimeout calls in our code and wondered why there is a setTimeout here. I'd love to remove it but I guess there is a reason why it was added in the first place. Does anyone know why it could be there and if it can be removed safely, so the code would look like this?

    var w = window.open('', 'print_window', 'width = 1000, height = 1000');
    w.document.write('<html><head><title>Printout</title>' + '<link rel="stylesheet" href="' + document.styleSheets[0].href + '"></head><body>');
    w.document.write(modal.dom.root.innerHTML);
    w.document.write('</body></html>');

    $(w.document.body).find('.modal-print').remove();
    w.document.close();
    w.focus();
    w.print();
    w.close();

My guess is that he is waiting for the stylesheet to load. So, maybe waiting for the DOM ready or load event would be a good replacement although I don't know if these would be fired after the stylesheet has been added since window.open() has been called before adding the code to the new window:

Ready event version:

    var w = window.open('', 'print_window', 'width = 1000, height = 1000');
    w.document.write('<html><head><title>Printout</title>' + '<link rel="stylesheet" href="' + document.styleSheets[0].href + '"></head><body>');
    w.document.write(modal.dom.root.innerHTML);
    w.document.write('</body></html>');

    // Will ready event be fired? If yes, at the right time?
    $(w.document).ready(function () {
        $(w.document.body).find('.modal-print').remove();
        w.document.close();
        w.focus();
        w.print();
        w.close();
    });

Version with load event:

    var w = window.open('', 'print_window', 'width = 1000, height = 1000');
    w.document.write('<html><head><title>Printout</title>' + '<link rel="stylesheet" href="' + document.styleSheets[0].href + '"></head><body>');
    w.document.write(modal.dom.root.innerHTML);
    w.document.write('</body></html>');

    // Will ready event be fired? If yes, at the right time?
    $(w).load(function () {
        $(w.document.body).find('.modal-print').remove();
        w.document.close();
        w.focus();
        w.print();
        w.close();
    });
Timo Ernst
  • 15,243
  • 23
  • 104
  • 165
  • 4
    Why not just test them? – Jonas Grumann Feb 01 '17 at 13:30
  • @JonasGiuro I'm currently trying the onload version and going to ask QA to test the implementation. However, I thought maybe someone on SO already has experience with this and knows which would be best practice. Also, I'm worried that users in different environments might get different results e.g. if their computer is slower than those we use to test. – Timo Ernst Feb 01 '17 at 13:35
  • takes time for that window to open – charlietfl Feb 01 '17 at 13:35
  • I see, anyway, use timeouts to wait things to load is always a bad idea, you don't know how long it will take so you're definitely on the right track with testing the "load" and "ready" events. – Jonas Grumann Feb 01 '17 at 13:40
  • You probably cant remove the setTimeout. SInce the opened window is a different DOM, there's not immediate a reliable way to tell you when the DOM content is loaded and hence, you'll be able to access the body. If you remove the timeout, you'll have the situation where half of the time it works, and the other half of the time it doesn't, depending on if the new window already finished its DOM when you try to query that DOM. I have absically the exact same code in my project and haven't found a way that works 100% of the time without using a timeout and a while loop. – Shilly Feb 01 '17 at 13:41
  • @Shilly So, this is not an option? http://stackoverflow.com/questions/3030859/detecting-the-onload-event-of-a-window-opened-with-window-open – Timo Ernst Feb 01 '17 at 13:53
  • That didn't work on IE9 for us. I would rewrite it with postMessage if I would code it today, but as the post suggests that introduces alot of domain issues, which would grow the function exponentionally and take away precious dev time. – Shilly Feb 01 '17 at 13:57

2 Answers2

0

Just for reference. The code I mentioned in my comment that we ended up using after being faced with the same issue:

function printHTML( html ) {
    var tab,
        interval;
    if (html) {
        tab = window.open($.ajax.host + $.ajax.directory + 'dist/res/print.html');
        interval = setInterval(function() {
            var body;
            // loop until the tab is opened, the tab has a document and the DOM has been parsed and is ready to be query'ed.
            if (tab && tab.document && tab.document.querySelector) body = tab.document.querySelector('body');
            if (body) {
                body.innerHTML = html.outerHTML;
                timeout again for the print function, so that the body has time to do a paint + reflow, so we are sure that the html has been written to the new tab.
                setTimeout(function() { 
                    // print and clean up
                    tab.print();
                    tab.close();
                    clearInterval(interval);
                }, 1)
            }
        }, 100);
    }
    else throw new Error("This report doesn't have a print version.");
}
Shilly
  • 8,511
  • 1
  • 18
  • 24
0

Instead of using a setTimeout to remove the section before it's printed, why not add the following style to the document...

<style media="print">
  .modal-print {
    display: none;
  }
</style>

This will hide the section, instead of removing it, but probably this is all you need.

You could also add...

<script>
  window.print();
  window.close();
</script>

Then you wouldn't need the setTimeout at all, because it would all be inside the document you've created.

Rik Lewis
  • 750
  • 4
  • 10
  • You don't need the `media="print"` if you want it to always be hidden, this would just hide it when it is printed. – Rik Lewis Feb 01 '17 at 13:58
  • This is indeed perfect for static pages, but for single page apps, it can create a mess. If you're not carefull, you might trick the app into changing state. – Shilly Feb 01 '17 at 13:59