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:
RequestContext.getCurrentInstance().execute("myFunction(...)")
in my action (yes, I'm using PrimeFaces).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:
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();
}
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();
}