100

In my application, I tried to print out a voucher page for the user like this:

  var htm ="<div>Voucher Details</div>";
  $('#divprint').html(htm);
  window.setTimeout('window.print()',2000);

'divprint' is a div in my page which store information about the voucher.

It works, and the print page pops up. But I want to advance the application once the user clicks 'print' or 'close' in the browser's pop-up print dialog.

For example, I'd like to redirect user to another page after pop up window is closed:

window.application.directtoantherpage();//a function which direct user to other page

How can I determine when the pop up print window is closed or print is finished?

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
JavaScripter
  • 4,282
  • 9
  • 37
  • 45

17 Answers17

149

You can listen to the afterprint event.

https://developer.mozilla.org/en-US/docs/Web/API/window.onafterprint

window.onafterprint = function(){
   console.log("Printing completed...");
}

It may be possible to use window.matchMedia to get this functionality in another way.

(function() {

    var beforePrint = function() {
        console.log('Functionality to run before printing.');
    };

    var afterPrint = function() {
        console.log('Functionality to run after printing');
    };

    if (window.matchMedia) {
        var mediaQueryList = window.matchMedia('print');
        mediaQueryList.addListener(function(mql) {
            if (mql.matches) {
                beforePrint();
            } else {
                afterPrint();
            }
        });
    }

    window.onbeforeprint = beforePrint;
    window.onafterprint = afterPrint;

}());

Source: http://tjvantoll.com/2012/06/15/detecting-print-requests-with-javascript/

Shofol
  • 693
  • 1
  • 11
  • 26
Adam
  • 43,763
  • 16
  • 104
  • 144
  • 3
    Keep in mind that the media query listener will fire `afterPrint()` immediately. It doesn't wait until the print dialog closes, which is why my answer listens for `mouseover` when using the media query solution... – quietmint Aug 20 '13 at 23:34
  • @user113215 How about on FF and IE? I think the native onafterprintevent might wait until after the dialog is closed. – Adam Aug 21 '13 at 18:11
  • Correct, at least in IE, `window.onafterprint` fires after the dialog closes. – quietmint Aug 22 '13 at 00:17
  • 2
    IE 7 doesn't (can't wait for it to fade out of existence). `onafterprint` is launched right after the print dialog opens. That was my experience, at least. – MPelletier Jun 09 '14 at 15:12
  • 12
    on IE 11 onafterprint fires before the print dialogue even comes up (and obviously, without having printed) – wintersylf Mar 22 '15 at 00:31
  • 2
    on IE 11 matchMedia exists but the listener never fires – wintersylf Mar 22 '15 at 00:38
  • Appears to be supported in Chrome v69.x. – Tim Newton Sep 20 '18 at 19:43
  • 4
    onafterprint is called regardless if the user confirmed the print. How can we tell if the user confirmed the printing? – Ian Kirkpatrick Aug 15 '19 at 17:55
  • As well as the problems others have highlighted, another that jumps out at me is that if a browser supports both `matchMedia` *and* `onafterprint`, this approach will call the `afterPrint` handler twice, which may well be problematic! – Mark Amery Jan 10 '20 at 13:19
  • Note that `mediaQueryList.addListener`is marked as deprecated and we should use `mediaQueryList.addEventListener` instead and watch for `change` event
    [MDN MediaQueryList](https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList)
    – Skunka Dec 03 '21 at 10:13
  • Thank you man, it works in Chrome 96.0.4664.110 and Firefox 96.0 with mediaQueryList.addEventListener. – federico__ Jan 12 '22 at 13:12
74

On chrome (V.35.0.1916.153 m) Try this:

function loadPrint() {
    window.print();
    setTimeout(function () { window.close(); }, 100);
}

Works great for me. It will close window after user finished working on printing dialog.

Sagreen
  • 749
  • 5
  • 2
  • Thanks to your answer I found an easy solution: I've added a new answer with ´window.open('', '_self', '');´ before print – Albert Català Mar 13 '15 at 08:53
  • 13
    The accepted answer works on very few browsers. Putting window.close directly after print causes the window to disappear before the print dialog. However, using setTimeout causes the close to be queued on the event queue, the 100ms timeout isn't actually a timing thing, the timer will be blocked until the print dialog is closed at which time the 100ms timer is started. A much better solution. – Luke Oct 26 '15 at 07:38
  • That's been working for me already 4 years, it seems like the time stops for the window that prints. – DavidTaubmann Feb 18 '16 at 20:49
  • @Luke Does "window.close()" even need to be there? Isn't the window closed when the user clicks 'print' or 'cancel'? Also how can i retrieve whichever button was clicked? – Peter Raeves Aug 09 '16 at 13:49
  • @PeterRaeves the questioner was talking about a popup. This is all about creating a window with the print data and printing that, rather than printing the current window. If you want to print the current window, you don't need to close. print() does not return anything and I'm unaware of any cross-browser function that may help in determining the button pressed on a print dialog. – Luke Aug 10 '16 at 01:23
  • 2
    Excellent! Works on Firefox 51.0.1 and Chrome 54.0.2840.90 – Autumn Leonard Feb 23 '17 at 16:15
  • 3
    This no longer works after Chrome v68 on Android. You need to extend the timeout to 500 milliseconds for the print dialog to come up and then, if you change the printer, the window has already closed and there is no longer ability for the browser to refresh. – Dyluck Dec 21 '18 at 20:59
  • Expanding on @Dyluck's issue above. On Windows Chrome, if you click on the "Print using the system dialogue...", the event loop picks up again, and your handler will be fired, when, technically, the printing hasn't happened yet. Makes sense, since the browser delegates to another system altogether, and maybe it cannot know when _that_ has finished, unless the OS talks back to the browser. – nevvermind May 21 '19 at 08:52
  • Works for all browsers (IE, Chrome and Firefox), unlike `onafterprint()`. Simplest by far. – Miroslav Glamuzina Jul 10 '19 at 14:38
  • The above solution don not work any more, please add event listener **onafterprint** to close window. `window.onafterprint = function(){ window.close()};` – Rahat Hameed Dec 31 '19 at 10:29
  • Good lord. Why isn't there a sane/standard method to detect when the print dialog closes (instead of having a bunch or Kluges)? Oh, yeah I'm stuck using JS... forgot. Thx for this solution though. Seems to work (for now). Chrome 94 on Linux Mint 20.2 – RyanNerd Oct 11 '21 at 11:52
  • This will fail if you need to use your printer's print dialog (Ctrl+Shift+P) in Chrome. – Edson Perotoni May 11 '22 at 17:44
11

compatible with chrome, firefox, opera, Internet Explorer
Note: jQuery required.

<script>

    window.onafterprint = function(e){
        $(window).off('mousemove', window.onafterprint);
        console.log('Print Dialog Closed..');
    };

    window.print();

    setTimeout(function(){
        $(window).one('mousemove', window.onafterprint);
    }, 1);

</script>
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
SAM
  • 111
  • 1
  • 4
6

window.print behaves synchronously on chrome .. try this in your console

window.print();
console.log("printed");

"printed" doesn't display unless the print dialog is closed(canceled/saved/printed) by the user.

Here is a more detailed explanation about this issue.

I am not sure about IE or Firefox will check and update that later

Rahil Ahmad
  • 3,056
  • 1
  • 16
  • 21
4

See https://stackoverflow.com/a/15662720/687315. As a workaround, you can listen for the afterPrint event on the window (Firefox and IE) and listen for mouse movement on the document (indicating that the user has closed the print dialog and returned to the page) after the window.mediaMatch API indicates that the media no longer matches "print" (Firefox and Chrome).

Keep in mind that the user may or may not have actually printed the document. Also, if you call window.print() too often in Chrome, the user may not have even been prompted to print.

Community
  • 1
  • 1
quietmint
  • 13,885
  • 6
  • 48
  • 73
3

You can detect when window.print() is finished simply by putting it in another function

//function to call if you want to print
var onPrintFinished=function(printed){console.log("do something...");}

//print command
onPrintFinished(window.print());

tested in Firefox,Google chrome,IE

  • 6
    What you are doing is simply passing the return value of window.print() to another function, when you call onPrintFinished of course window.print() will be executed first, but only if the browser provides a synchronous print() method (latest firefox on linux provides this for example) the onPrintFinished function will be execute AFTER the print dialog is closed, on other browsers (e.g. latest Chromium) the print() method is async so this is useless. – emerino Jun 07 '14 at 22:39
  • Modern Chrome, Safari, FireFox... all support doing it this way. – FactoryAidan Mar 22 '18 at 11:25
  • Works in the latest version of chrome – Josh Jul 02 '19 at 18:41
  • I don't thing that is excellent approach to solve this issue, please add event listener **onafterprint** to close window. `window.onafterprint = function(){ window.close()};` – Rahat Hameed Dec 31 '19 at 10:29
  • this will fail if you need to use your printer's print dialog (Ctrl+Shift+P) – Edson Perotoni May 11 '22 at 16:34
  • This is a complicated way to write `window.print(); console.log("do something...");`. – bfontaine Jan 26 '23 at 16:23
3

Simplest way to detect if print has finished and close print window:

window.onafterprint = function(){ 
  window.onfocus = function(){  
    window.close();
  }
};
Osei-Owusu
  • 201
  • 3
  • 6
2

This Actually worked for me in chrome. I was pretty suprised.

jQuery(document).bind("keyup keydown", function(e){
    if(e.ctrlKey && e.keyCode == 80){
         Print(); e.preventDefault();
    }
});

Where Print is a function I wrote that calls window.print(); It also works as a pure blocker if you disable Print();

As noted here by user3017502

window.print() will pause so you can add an onPrintFinish or onPrintBegin like this

function Print(){
    onPrintBegin
    window.print();
    onPrintFinish(); 
}
Jack Franzen
  • 768
  • 7
  • 21
2

Tested IE, FF, Chrome and works in all.

    setTimeout(function () { window.print(); }, 500);
    window.onfocus = function () { setTimeout(function () { window.close(); }, 500); }
Pakk
  • 1,299
  • 2
  • 18
  • 36
  • There are some details missing here, but filling them in as best I can, this still doesn't work for me (in Chrome 80 on Ubuntu 19.10). If you're printing the page you already have focus on, it doesn't lose focus at any point, and so `onfocus` doesn't fire after you're done. If you're printing an `iframe`, the page you're on loses focus, but doesn't automatically regain it after printing is done, so this still doesn't quite work. – Mark Amery Jan 10 '20 at 14:59
2

Given that you wish to wait for the print dialog to go away I would use focus binding on the window.

print();

var handler = function(){
    //unbind task();
    $(window).unbind("focus",handler);
}

$(window).bind("focus",handler);

By putting in the unbind in the handler function we prevent the focus event staying bond to the window.

kunicmarko20
  • 2,095
  • 2
  • 15
  • 25
1

Print in new window with w = window.open(url, '_blank') and try w.focus();w.close(); and detect when page is closed. Works in all browsers.

w = window.open(url, '_blank');
w.onunload = function(){
 console.log('closed!');
}
w.focus();
w.print();
w.close();

Window close after finish print.

e-info128
  • 3,727
  • 10
  • 40
  • 57
  • on IE 11 this worked for me but only the first time. every subsequent time I open the window it automatically closes without giving me a print dialogue – wintersylf Mar 22 '15 at 00:20
  • Is a bug of Internet Explorer. Report to Microsoft and waits for them to have the patch or use other alternatives. – e-info128 May 19 '17 at 15:36
  • It's not just Internet Explorer this will fail in. In Chrome, this will usually print a blank page, because `w.print()` happens asynchronously and you've yanked the page out from under its feet with `w.close()` before it's actually gotten ready to print it. – Mark Amery Jan 10 '20 at 15:02
1

It works for me with $(window).focus().

var w;
var src = 'http://pagetoprint';
if (/chrom(e|ium)/.test(navigator.userAgent.toLowerCase())) {
    w = $('<iframe></iframe>');
    w.attr('src', src);
    w.css('display', 'none');
    $('body').append(w);
    w.load(function() {
        w[0].focus();
        w[0].contentWindow.print();
    });
    $(window).focus(function() {
        console.log('After print');
    });
}
else {
    w = window.open(src);
    $(w).unload(function() {
        console.log('After print');
    });
}
Kjeld
  • 131
  • 1
  • 3
0

I think the window focus approach is the correct one. Here is an example in which I wanted to open a PDF url blob in a hidden iframe and print it. After printed or canceled, I wanted to remove the iframe.

/**
 * printBlob will create if not exists an iframe to load
 * the pdf. Once the window is loaded, the PDF is printed.
 * It then creates a one-time event to remove the iframe from
 * the window.
 * @param {string} src Blob or any printable url.
 */
export const printBlob = (src) => {
  if (typeof window === 'undefined') {
    throw new Error('You cannot print url without defined window.');
  }
  const iframeId = 'pdf-print-iframe';
  let iframe = document.getElementById(iframeId);
  if (!iframe) {
    iframe = document.createElement('iframe');
    iframe.setAttribute('id', iframeId);
    iframe.setAttribute('style', 'position:absolute;left:-9999px');
    document.body.append(iframe);
  }
  iframe.setAttribute('src', src);
  iframe.addEventListener('load', () => {
    iframe.contentWindow.focus();
    iframe.contentWindow.print();
    const infanticide = () => {
      iframe.parentElement.removeChild(iframe);
      window.removeEventListener('focus', infanticide);
    }
    window.addEventListener('focus', infanticide);
  });
};
Eric H.
  • 6,894
  • 8
  • 43
  • 62
  • 1
    I am having a problem `exports is not defined` . – aldoblack Mar 07 '18 at 16:48
  • This doesn't directly address the question asked. Reading between the lines, I guess we could add code to the `infanticide` function if we want it to run after printing. But that doesn't work quite right either; at least in my browser (Chrome 80 on Ubuntu 19.10), the window doesn't get focus back immediately after printing - only when I click or tab back into it. – Mark Amery Jan 10 '20 at 14:52
0

It is difficult, due to different browser behavior after print. Desktop Chrome handles the print dialogue internally, so doesn't shift focus after print, however, afterprint event works fine here (As of now, 81.0). On the other hand, Chrome on mobile device and most of the other browsers shifts focus after print and afterprint event doesn't work consistently here. Mouse movement event doesn't work on mobile devices.

So, Detect if it is Desktop Chrome, If Yes, use afterprint event. If No, use focus based detection. You can also use mouse movement event(Works in desktop only) in combination of these, to cover more browsers and more scenarios.

Deb
  • 5,163
  • 7
  • 30
  • 45
0

well, just to remind everyone that the afterprint will not determine the print action button instead it will execute whenever the print window is closed or closing, both cancel button or esc key which closing the print window will consider afterprint while there is no actual print happening yet.

  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/33318809) – ahuemmer Dec 07 '22 at 08:54
0

I offer a solution for people who have the same problem as me. I am using Chrome browser, and in my project, I create an iframe to print PDF. However, when I bind the afterprint event, it doesn't work.

The following code detects if printing is completed by binding the focus event.

  const iframe = document.createElement('iframe');
  iframe.style.display = 'none';
  iframe.src = url;
  iframe.onload = function () {
    console.log('load iframe done')
    const handle = () => {
      window.removeEventListener('focus', handle)
      // do something...
    }
    window.addEventListener('focus', handle)
    iframe.focus()
    iframe.contentWindow.print()
  }
  document.body.appendChild(iframe)

Simon
  • 1
-1

Implementing window.onbeforeprint and window.onafterprint

The window.close() call after the window.print() is not working in Chrome v 78.0.3904.70

To approach this I'm using Adam's answer with a simple modification:

     function print() {
    (function () {
       let afterPrintCounter = !!window.chrome ? 0 : 1;
       let beforePrintCounter = !!window.chrome ? 0 : 1;
       var beforePrint = function () {
          beforePrintCounter++;
          if (beforePrintCounter === 2) {
             console.log('Functionality to run before printing.');
          }
       };
       var afterPrint = function () {
          afterPrintCounter++;
          if (afterPrintCounter === 2) {
             console.log('Functionality to run after printing.');
             //window.close();
          }
       };
       if (window.matchMedia) {
          var mediaQueryList = window.matchMedia('print');
          mediaQueryList.addListener(function (mql) {
             if (mql.matches) {
                beforePrint();
             } else {
                afterPrint();
             }
          });
       }
       window.onbeforeprint = beforePrint;
       window.onafterprint = afterPrint;
    }());
    //window.print(); //To print the page when it is loaded
 }

I'm calling it in here:

<body onload="print();">

This works for me. Note that I use a counter for both functions, so that I can handle this event in different browsers (fires twice in Chrome, and one time in Mozilla). For detecting the browser you can refer to this answer

juan_carlos_yl
  • 641
  • 8
  • 14
  • Far better would be to check for `onafterprint`, use it if it exists, and otherwise attempt the `matchMedia` hack. Your way, you'll never call your handler in old versions of Chrome where (if the accepted answer by Adam can be believed) the `matchMedia` approach works but `onafterprint` doesn't, and you'll call it *twice* in any browser besides Chrome where both approaches work. – Mark Amery Jan 10 '20 at 14:42