【问题标题】:JavaFX WebView: link to anchor in document doesn't work using loadContent()JavaFX WebView:链接到文档中的锚点无法使用 loadContent()
【发布时间】:2018-03-02 14:03:57
【问题描述】:

注意:这是关于 JavaFX WebView,而不是 Android WebView(即我见过“Android Webview Anchor Link (Jump link) not working”)。


我在 javafx.scene.web.WebView 内显示一个生成的 HTML 页面,其中包含锚点和指向这些锚点的链接,如下所示:

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

我使用这段代码将 HTML 加载到 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);
    }
}

一切都正确呈现,但如果我点击名为“介绍”的链接,什么都没有发生。

但是 HTML 是正确的,我使用以下代码进行了检查:

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

现在,一切正常。

问题似乎是因为使用loadContent()时WebView的文档URL是null,但由于它是只读属性,我不知道如何使它工作。

我需要使用loadContent(),因为 HTML 是动态生成的,如果可能的话,我不想为了使锚链接工作而将其写入文件。有没有办法解决这个问题?


编辑 我为 JavaFX 提交了bug

【问题讨论】:

    标签: html javafx webview


    【解决方案1】:

    这可能是另一个 WebEngine 错误。很多代码只是包装在 api 中的本机库,因此我们无法在运行时对其进行修改以修复一些缺陷。

    如果你能够改变生成文件的结构,你可以在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>
    

    如果您无法更改结构,我已经采取了一些步骤来尝试修复它并提出了一些建议 - 起初我通过在 loadContent 之后的反射设置了 location 的值:

    Field locationField = WebEngine.class.getDeclaredField("location");
    locationField.setAccessible(true);
    
    ReadOnlyStringWrapper location = (ReadOnlyStringWrapper) locationField.get(engine);
    location.set("local");
    

    但事实上,保持实际位置的状态对您来说只是一个信息,操纵它不会改变任何事情。我还找到了一种从 js 设置 url 的方法(只是一个长镜头,我们没有任何具体细节为什么它不起作用):

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

    当然不能,因为:

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

    我认为你应该忘记loadContent()。您说您不想将生成的内容写入文件。一个小肮脏的黑客,但对你真的很有帮助,可以在你的应用程序中将http服务器包装在随机和未使用的端口上。你甚至不需要外部库,因为 Java 有这样的简单实用程序:

    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();
    

    您也可以使用其他浏览器来显示您的页面,例如JCEFJava Chromium 嵌入式框架)。

    【讨论】:

    • 哇,感谢您提供如此详尽的答案。我已经针对 WebView 提交了一个错误 - 如果我们至少不报告我们发现的错误,我们不能指望它会变得更好。顺便说一句,恕我直言,它已经有了很大的改进。使用 HttpServer 很有趣,但我想我会先尝试您建议的 JavaScript 解决方法。
    • 在使用 WebEngine 时经常会发生某些事情无法正常工作的情况。这是一个非常有用的组件,但它仍处于早期开发阶段(如果我们将其与铬或量子等引擎进行比较),因此我们必须容忍一些变通方法。
    • 使用的webkit引擎已经有15年的历史了,webview也已经存在了至少7年...
    • 是的,但它只是一个核心,而不是您看到的输出 - “默认情况下,WebKit 不支持网页渲染。为了渲染和显示 HTML 内容,Oracle 必须编写自己的渲染器使用 Java Graphics 2D API。”
    • 您建议的 JavaScript 解决方案有效。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    • 1970-01-01
    • 2015-12-17
    • 2012-12-13
    • 1970-01-01
    • 2012-07-25
    • 1970-01-01
    相关资源
    最近更新 更多