javascriptjsfprimefacespopuppopup-blocker

How to open a pop-up using JSF without it being blocked by the browser


I'm working on a PrimeFaces 6.0, JSF 2.2 (Mojarra 2.2.7) application.

I need to load a web page from an external site and highlight a DOM node. My approach is to create a JavaScript function to open a pop-up window, load the web page through a servlet (to avoid cross domain issues) and highlight the node. The parameters I send to my function are generated in a managed bean.

I tried to do so in two different ways:

  1. Using RequestContext.getCurrentInstance().execute("myFunction(...)") in my action (yes, I'm using PrimeFaces).
  2. Using oncomplete="#{myBean.myJsCall}" on my command button.

Both ways the call is executed and the call is correct, but I run into my browser's (Chromium) pop-up blocker:

The following pop-ups were blocked on this page

Is there a way to open pop-ups in JSF or specifically in PrimeFaces without them being blocked?

This is not really relevant, but this is the simplified version of my JavaScript function.

I developed this script using plain HTML and JS. There it was opening the pop-up without the blocker interfering. Also, when pasting the call into the console when running the JSF application, the pop-up is opened.

function myFunction(url, selector) {
    var popup = window.open("", "popup", "height=500,width=700");
    var req = new XMLHttpRequest();
    req.open("GET", url, true);
    req.onreadystatechange = function() {
        if (req.readyState === XMLHttpRequest.DONE) {
            popup.document.open();
            popup.document.write(req.responseText);
            popup.document.close();
            popup.document.addEventListener(
                "DOMContentLoaded",
                function() { /* Some code highlighting the selector */ },
                false
            );
        }
    }
    req.send();
}

Solution

  • When you try to open a new window in JavaScript, which is not directly triggered by a user action (like a click), but in for example a callback triggered after executing JSF ajax requests, it is blocked by the browser.

    This behavior is described in the following question: jquery window.open in ajax success being blocked.

    As a workaround you could open a window before the JSF ajax request. In case of a command button using the JSF HTML tags this can be done by using:

    <h:commandButton ... onclick="prepareWindow()"/>

    The onclick will be rendered as is. But, when you are using PrimeFaces, you cannot use:

    <p:commandButton ... onclick="prepareWindow()"/>

    PrimeFaces wraps the onclick resulting in indirect execution, so the pop-up is blocked.

    Anyhow, with some extra hacks you could get this working with some sort of button which looks like a PrimeFaces button. But the number of hacks was getting too much.

    I opted to use a p:dialog with an iframe instead. To start of, if you are using p:layout, don't place the dialog in any of the layout units.

    The dialog:

    <p:dialog header="Preview"
              widgetVar="previewDlg" modal="true" width="1000" height="600"
              dynamic="true">
        <iframe src="about:blank"
                class="previewIframe"
                style="position:absolute;top:0;left:0;right:0;bottom:0;width:100%;height:100%;border:0"></iframe>
    </p:dialog>
    

    The button:

    <p:commandButton value="Show"
                     ...
                     onclick="PF('previewDlg').show()"
                     action="#{myBean.showPreview('previewIframe')}"/>
    

    The action:

    public void showPreview(String iframeClass) {
        ...
        RequestContext.getCurrentInstance().execute(js);
    }
    

    The JavaScript function:

    function myFunction(iframeClass, url, selector) {
        var iframe = $("iframe[class=" + iframeClass + "]")[0];
        iframe.contentDocument.location = "about:blank";
        var req = new XMLHttpRequest();
        req.open("GET", url, true);
        req.onreadystatechange = function() {
            if (req.readyState === XMLHttpRequest.DONE) {
                iframe.contentDocument.open();
                iframe.contentDocument.write(req.responseText);
                iframe.contentDocument.close();
                iframe.contentDocument.addEventListener(
                    "DOMContentLoaded",
                    function() { /* Some code highlighting the selector */ },
                    false
                );
            }
        }
        req.send();
    }