How do I make the background of a WebView transparent?

Tags: , , ,



I have a Maven project in IntelliJ IDEA, using JDK 15 and JavaFX 15.

I tried:

  1. Set style for WebEngine
  2. Set blend mode for WebView
  3. Tried WebPage

Nothing succeeded. How do I make the background of the WebView transparent?

My module-info.java:

module project {
    requires javafx.controls;
    requires javafx.web;
    requires javafx.graphics;
    requires uk.co.caprica.vlcj;
    requires uk.co.caprica.vlcj.javafx;

    exports project;
}

My pom.xml dependencies:

<dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>15</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-web</artifactId>
            <version>15</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-graphics</artifactId>
            <version>15</version>
        </dependency>
        <dependency>
            <groupId>uk.co.caprica</groupId>
            <artifactId>vlcj</artifactId>
            <version>4.7.0</version>
        </dependency>
        <dependency>
            <groupId>uk.co.caprica</groupId>
            <artifactId>vlcj-javafx</artifactId>
            <version>1.0.2</version>
        </dependency>
</dependencies>

I created a new project and tried to implement the example from the answer. Happened. Unfortunately, the background color does not become transparent after clicking the button. Do you have any idea why the example didn’t work?

Main.java:

import javafx.application.Application;
import javafx.stage.Stage;

import com.sun.webkit.dom.HTMLDocumentImpl;
import java.lang.reflect.Field;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.web.WebView;

public class Main extends Application {

    @Override
    public void start(final Stage stage) {
        initStage(stage);
    }

    private void initStage(Stage stage){
        WebView wv = new WebView();
        wv.setStyle("-fx-background-color: transparent;");

        wv.getEngine().load("https://w3schools.com");
        VBox vb = new VBox(wv);
        vb.setBackground(new Background(new BackgroundFill(Color.ORANGERED, CornerRadii.EMPTY, Insets.EMPTY)));

        Button b = new Button("remove page background");

        b.setOnAction((event) -> {
            HTMLDocumentImpl cast = (HTMLDocumentImpl) wv.getEngine().getDocument();

            try {
                Field f = wv.getEngine().getClass().getDeclaredField("page");
                f.setAccessible(true);
                com.sun.webkit.WebPage page = (com.sun.webkit.WebPage) f.get(wv.getEngine());
                page.setBackgroundColor((new java.awt.Color(1, 1, 1, 1)).getRGB());
                f.setAccessible(false);
            } catch (Exception e) {
            }

        });
        vb.getChildren().add(b);

        stage.setScene(new Scene(vb));
        stage.show();
    }

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

VM options:

--module-path ${PATH_TO_FX} --add-modules javafx.controls,javafx.web --add-exports javafx.web/com.sun.webkit.dom=ALL-UNNAMED

enter image description here

Answer

The WebView renders the DOM CSS in the web page. If you want a transparent background, you’ll have to modify the CSS there as well.

EDIT … the following works, but opens up a can of worms.

enter image description here

    @Override
    public void start(Stage primaryStage) throws Exception {
    WebView wv = new WebView();
    wv.setStyle("-fx-background-color: transparent;");

    wv.getEngine().load("https://w3schools.com");
    VBox vb = new VBox(wv);
    vb.setBackground(new Background(new BackgroundFill(Color.ORANGERED, CornerRadii.EMPTY, Insets.EMPTY)));

    Button b = new Button("remove page background");

    b.setOnAction((event) -> {
        HTMLDocumentImpl cast = HTMLDocumentImpl.class.cast(wv.getEngine().getDocument());
        Node item = cast.getElementsByTagName("html").item(0);

        try {
            // Use reflection to retrieve the WebEngine's private 'page' field. 
            Field f = wv.getEngine().getClass().getDeclaredField("page");
            f.setAccessible(true);
            com.sun.webkit.WebPage page = (com.sun.webkit.WebPage) f.get(wv.getEngine());
            page.setBackgroundColor((new java.awt.Color(0, 0, 0, 0)).getRGB());
            f.setAccessible(false);
        } catch (Exception e) {
        }

    });
    vb.getChildren().add(b);

    primaryStage.setScene(new Scene(vb));
    primaryStage.show();
}

added import list

import com.sun.webkit.dom.HTMLDocumentImpl;
import java.lang.reflect.Field;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import org.w3c.dom.Node;

you may need to add stuff like :

--add-exports=javafx.web/com.sun.webkit.dom=ALL-UNNAMED 

in your run.jvm args parameter of the project.properties file.

note… this is kind of a hack and is not encouraged by oracle.

EDIT (2)

enter image description here

Again, this is kind of a hack and is incomplete at best.

This snapshot is taken after scrolling down immediately after removing the background.

Technically, what I think is going on is that whenever the DOM render engine redraws a frame, it is expected to draw everything hierarchically, and the background is never expected to be transparent. As such it doesn’t “clear” stuff and it simply draws the foreground on top of itself repeatedly.

Additionally, there is 1 interesting thing that’s happening.

The page background gets remove if you rClick + reloadpage, or if you click the “accept cookies” thing…. which i’m pretty sure triggers a dom update.

So what is evidently going on is that the WebView and WebPage components are part of 2 distinct universes. WebView is DOM manipulation… WebPage is something which the DOM uses during specific phases which deal with rendering.

I’m probably going to get this account suspended if I voice my opinion on why functionality in WebPage is “hidden” … but from personal experience, whenever things are difficult to get a hold of, such as WebPage, in openJFx, it usually means you’ll waste months trying to find a solution for something which will most likely be changed sometime in the future, thereby making your change obsolete.

My advice is to either abandon this problem, or change your mindset entirely.

I think what you should do is essentially emulate the rendering in something like a pane.

so… new Stage… PaneX as Scene, parse whatever is in the webview, and use the css and whatnot to put nodes in the PaneX which essentially look and feel the same as what’s in the webview. … as you can imagine… this will be a pane in the ass…

Alternatively, you might want to consider getting a hold of the openjfx codebase and use modified sources in order to get WebPage to effectively clear itself by adding extra code … but it’s up to you.

Lastly, you have to consider that this is done as is for a reason which probably revolves around performance issues.

Say for example, instead of clearing just the background in the webpage, you remove any and all backgrounds in a CSS in a page, you’ll have to do this a lot. which means performance issues since it essentially implies full dom tree parsing at page load.



Source: stackoverflow