简体   繁体   中英

JavaFX WebView: link to anchor in document doesn't work using loadContent()

Note: This is about JavaFX WebView, not Android WebView (ie I have seen " Android Webview Anchor Link (Jump link) not working ").


I display a generated HTML page inside a javafx.scene.web.WebView that contains anchors and links to those anchors like this:

<p>Jump to <a href="#introduction">Introduction</a></p>
some text ...
<h1 id="introduction">Introduction</h1>
more text ...

I use this code to load the HTML into the WebView:

public void go(String location) {
    try {
        // read the content into a String ...
        String html = NetUtil.readContent(new URL(location), StandardCharsets.UTF_8);
        // ... and use loadContent()
        webview.getEngine().loadContent(html);
    } catch (IOException e) {
        LOG.error(e);
    }
}

Everything is rendered correctly, but if I click on the link named "Introduction", nothing happens.

The HTML however is correct, which I checked by instead using this code:

public void go(String location) {
    // use load() to directly load the URL
    webview.getEngine().load(location);
}

Now, everything worls fine.

The problem seems to be somehow because the document URL of the WebView is null when using loadContent() , but since it's a readonly property, I have no idea how to make it work.

I need to use loadContent() , because the HTML is generated on the fly, and if possible in any way, I don't want to have to write it out to a file just to make anchor links working. Is there a way to fix this?


EDIT I filed a bug for JavaFX.

It's probably another WebEngine bug . A lot of that code is just a native libraries wrapped in api, so we can't modify it in runtime to fix some disabilities.

If you are able to change the structure of generated file you can implement scrolling to element in js:

<script>
function scrollTo(elementId) {
    document.getElementById(elementId).scrollIntoView(); 
}
</script>

<a href='#' onclick=scrollTo('CX')>Jump to Chapter X</a>
<h2 id="CX">Chapter X</h2>

If you can't change the structure, there is some steps that I've made to try to fix it and some suggestions - at first I've set value of location by reflections after loadContent for sure:

Field locationField = WebEngine.class.getDeclaredField("location");
locationField.setAccessible(true);

ReadOnlyStringWrapper location = (ReadOnlyStringWrapper) locationField.get(engine);
location.set("local");

But in fact, keeping state of actual location is just an information for you and manipulating this changes nothing. I've also found a way to set url from js (just a long shot, we don't have any specific details why it's not working):

window.history.pushState("generated", "generated", '/generated');

Of course we can't because of:

SecurityError: DOM Exception 18: An attempt was made to break through the security policy of the user agent.

I think you should forget about loadContent() . You said that you didn't want to write generated content to file. A little dirty hack but really helpful for you could be wrapped http server on random and unused port in your application. You don't even need external libraries because Java has simple utilities like that:

HttpServer server = HttpServer.create(new InetSocketAddress(25000), 0);
server.createContext("/generated", httpExchange -> {
    String content = getContent();
    httpExchange.sendResponseHeaders(200, content.length());
    OutputStream os = httpExchange.getResponseBody();
    os.write(content.getBytes());
    os.close();
});

server.setExecutor(null);
server.start();

You can also use another browser to display your page, eg JCEF ( Java Chromium Embedded Framework ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM