I'm trying to use a webview in a JavaFX based UI. The content of webview has to be changed by calling its .load(...)
method whenever some actions happen. after each load, I have to set some different values on the window object in webview. These values are then used in an JavaScript code which is run on ready event of document using jQuery:
$(document).ready(function() {
// here I use values set on window to populate/manipulate HTML elements
});
I'm following Oracle's guide to get window object and use setMember
on it to pass the values I need as described here. However I have been experiencing inconsistencies when the webView.load
is called for the first time compared to when it is called afterwards. The problem is that for the first time the values can be set on window object before content is loaded. This means that JavaScript will have the data it needs. On consequent calls however, I can't find a way to set the values on the right moment. The values are either set too early on the old window object and therefore removed after document change or are set too late and after JavaScript is finished running.
I tried setting values on both webView.getEngine().getLoadWorker().stateProperty().addListener(...)
and webEngine.documentProperty().addListener(...)
, the former being too early and the latter too late.
I found one solution, to call an explicit JavaScript function on webEngine.documentProperty().addListener(...)
. But this prevents me from sharing the same UI between web and Java project. It is important to find a way for fixing this (I would say) bug and have a proper consistent functionality.
I know similar questions have been asked before but for non of them it was not important to have the exact same behaviour as on the web.
UPDATE adding a small example to further clarify the problem:
public class WebEngineLoadBug extends Application {
private WebEngine webEngine;
private WebView webView;
/**
* @param args ignored.
*/
public static void main(final String[] args) {
launch(args);
}
@Override
public void start(final Stage primaryStage) throws Exception {
webView = new WebView();
webEngine = webView.getEngine();
// alert is the safest way to pass on data
webEngine.setOnAlert(new EventHandler<WebEvent<String>>() {
@Override
public void handle(WebEvent<String> event) {
System.out.println(event.getData());
}
});
primaryStage.setScene(new Scene(webView));
primaryStage.show();
load1();
}
public void load2() {
System.out.println("loading 2 ...");
// second load
JSObject dom2Window = (JSObject) webEngine.executeScript("window");
dom2Window.setMember("variable2Name", "variable value 2");
dom2Window.setMember("ctr", this);
// this load does NOT work as expected, variable2Name is NOT set before JavaScript runs and it can NOT be accessed in document ready event
webEngine.load(WebEngineLoadBug.class.getResource("/file2.html").toExternalForm());
}
public void load1() {
System.out.println("loading 1 ...");
// first load
JSObject domWindow = (JSObject) webEngine.executeScript("window");
domWindow.setMember("variableName", "variable value 1");
domWindow.setMember("ctr", this);
// this load works perfectly, variableName is set before JavaScript runs and it can be accessed in document ready event
webEngine.load(WebEngineLoadBug.class.getResource("/file1.html").toExternalForm());
}
}
this is file1.html
:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-2.2.1.min.js"></script>
</head>
<body>
<h1 id="title">FILE 1</h1>
<a href="#" onclick="event.preventDefault(); ctr.load2();">load 2</a>
<script>
alert('file1');
$(document).ready(function () {
// use variableName here
$("#title").html(variableName);
alert(variableName);
});
</script>
</body>
</html>
here comes file2.html
:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-2.2.1.min.js"></script>
</head>
<body>
<h1 id="title">FILE 2</h1>
<a href="#" onclick="event.preventDefault(); ctr.load1();">load 1</a>
<script>
alert('file2');
$(document).ready(function () {
// use variableName here
$("#title").html(variable2Name);
alert(variable2Name);
});
</script>
</body>
</html>
the problem is visible on the console where you will see these lines:
loading 1 ...
file1
variable value 1
loading 2 ...
file2
the problem is the missing variable value 2
here and also the title of second page is not loaded. Even if you navigate to pages forth and back, you will see that values are not passed correctly any more.