6

I tried to create a simple example of callback from Javascript to Java, based on the last example in WebEngine's javadoc (Calling back to Java from JavaScript). But when I click the link in the WebView, the Java method is not called and the page disappears.

public class TestOnClick extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        try {
            final WebView webView = new WebView();
            final WebEngine webEngine = webView.getEngine();

            Scene scene = new Scene(webView);

            stage.setScene(scene);
            stage.setWidth(1200);
            stage.setHeight(600);
            stage.show();

            String webPage = "<html>\n"
                    + "    <body>\n"
                    + "        <a href=\"\" onclick=\"app.onClick()\">Click here</a>\n"
                    + "    </body>\n"
                    + "</html>";

            System.out.println(webPage);

            webView.getEngine().loadContent(webPage);

            JSObject window = (JSObject) webEngine.executeScript("window");
            window.setMember("app", new JavaApp());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }

    public static class JavaApp {

        public void onClick() {
            System.out.println("Clicked");
        }
    }
}

Note: I don't see any exceptions being thrown in the WebView when monitoring the load worker with webView.getEngine().getLoadWorker().exceptionProperty().addListener(...).

assylias
  • 321,522
  • 82
  • 660
  • 783
  • **Note for readers:** Starting from 2.2.5 the bridge class (`JavaApp` in this case) must be declared `public`. – spongebob Nov 30 '16 at 19:52

1 Answers1

6

You are trying to access webview DOM model before it was created.

Wrap your JavaApp related code to the page load listener to achieve your goal:

webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
    @Override
    public void changed(ObservableValue<? extends State> ov, State t, State t1) {
        if (t1 == Worker.State.SUCCEEDED) {
            JSObject window = (JSObject) webEngine.executeScript("window");
            window.setMember("app", new JavaApp());
        }
    }
});
Sergey Grinev
  • 34,078
  • 10
  • 128
  • 141
  • 1
    This apporach introduces a race condition if loaded page needs to access some application API at startup, because page JavaScript is executed before `RUNNING` -> `SUCCEEDED` transition happens. – Maxim Dec 26 '15 at 18:37
  • to solve this classic problem of concurrent access you can use any standard approaches: e.g. wait in js till variable app is initialised. – Sergey Grinev Dec 28 '15 at 14:17