This tutorial shows how to build a small Java application that listens to changes occurred in an HTML page loaded into JxBrowser.

Prerequisites

To go through this tutorial you will need:

  • Git
  • Java 8 or higher
  • A valid JxBrowser license. It can be either Evaluation or Commercial. For more information on licensing please see the Licensing guide.

Setting Up a Project

The code of the example application for this tutorial is available along with other examples from a GitHub repository as a Gradle-based project.

If you want to build a Maven-based project, please refer to the Maven Config guide. If you would like to build a Gradle-based project from scratch, please see the Gradle Config guide.

Getting the Code

To get the code please execute the following commands:

$ git clone https://github.com/TeamDev-IP/JxBrowser-Examples
$ cd JxBrowser-Examples/tutorials/content-changes

Now we are in the root directory of all examples. The code of this tutorial is under the tutorials/content-changes directory.

Adding the License

Please put the license.jar under the JxBrowser-Examples directory. The build script relies on presence of this file in the root of the project.

The Page

We are going to load a simple HTML page on which we will display a counter auto-incrementing every second.

Here is the code of the counter:

<div>
    <span class="counter" id="counter"></span>
</div>

The counter is updated using jQuery:

<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" crossorigin="anonymous"></script>
<script type="text/javascript">
    $(document).ready(function () {
        var counter = 1;
        setInterval(function() { $(".counter").text(counter++); }, 1000);
    });
</script>

Here is the full code of the page, which is included into the project as the resource file named index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<div>
    <span class="counter" id="counter"></span>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" crossorigin="anonymous"></script>
<script type="text/javascript">
    $(document).ready(function () {
        var counter = 1;
        setInterval(function() { $(".counter").text(counter++); }, 1000);
    });
</script>
</body>
</html>

The JavaScript Code

As we want to notify the Java code of our application from within our HTML code, we need the JavaScript code that handles DOM modifications and passes them on to our Java code. We could have done it right in the HTML code, but we want to show how to add such a code dynamically from Java.

First, we obtain the element to track:

const element = document.getElementById('counter');

Then we create a MutationObserver instance with a callback to pass the data to the Java code.

const observer = new MutationObserver(
    function(mutations) {
        window.java.onDomChanged(element.innerHTML);
    });

The most important part here is this call:

window.java.onDomChanged(element.innerHTML);

Here we reference an object stored in the window object as the property named java is the name of the property which contains the Java object to be called. We use the word java to highlight the fact that we are calling a Java object. It can be any JavaScript identifier that matches the sense of your application.

The method of the object that we are calling is onDomChanged(). Later we will add this method when we create a Java class for listening to content changes. We pass innerHTML property of the counter element. So, the method will accept a String parameter.

Now, let’s tell the observer to track DOM changes:

const config = {childList: true};
observer.observe(element, config);

Here is the full JavaScript code that we put in resources as observer.js file:

const element = document.getElementById('counter');
const observer = new MutationObserver(
    function(mutations) {
        window.java.onDomChanged(element.innerHTML);
    });
const config = {childList: true};
observer.observe(element, config);

The Java Code

Utility for Loading Resources

In previous sections we reviewed HTML and JavaScript code stored as resources. Now we need the code that loads them. We will use the simplest approach using the Resources utility class from Guava.

Here is the code of the utility method load() that we will use later:

    private static String load(String resourceFile) {
        URL url = Resources.getResource(resourceFile);
        String result;
        try {
            result = Resources.toString(url, Charsets.UTF_8);
        } catch (IOException e) {
            throw new IllegalStateException("Unable to load resource " + resourceFile, e);
        }
        return result;
    }

Creating Browser and BrowserView

For the sake of simplicity of the example we will put all the code under the main() method. A real application would have more structured code.

First of all, we need to create a Browser and a BrowserView:

        Browser browser = new Browser();
        BrowserView view = new BrowserView(browser);

Then we create a JFrame and add newly created BrowserView to it.

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(view, BorderLayout.CENTER);
        frame.setSize(700, 500);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

Now we need to create an object that listens to DOM changes.

Java Object for Listening DOM Changes

As you may remember, the JavaScript code we reviewed earlier needed an object with the method named onDomChanged() which accepts String argument.

Here is the class:

    public static class JavaObject {

        @SuppressWarnings("unused") // invoked by callback processing code.
        public void onDomChanged(String innerHtml) {
            System.out.println("DOM node changed: " + innerHtml);
        }
    }

Now we need to make JavaScript code to talk to our Java object. Let’s do it.

Wiring JavaScript and Java

In order to make JavaScript code talk to our Java object, we will implement a ScriptContextAdapter passing the instance to Browser.addScriptContextListener().

        browser.addScriptContextListener(new ScriptContextAdapter() {
            @Override
            public void onScriptContextCreated(ScriptContextEvent event) {
                Browser browser = event.getBrowser();
                JSObject window = browser.executeJavaScriptAndReturnValue("window")
                                         .asObject();
                window.setProperty("java", new JavaObject());
                String javaScript = load("observer.js");
                browser.executeJavaScript(javaScript);
            }
        });

In this code we:

  1. Obtain an instance of the JavaScript window object.
  2. Create a Java object that listens to content changes and set it as a property named java in the window. Earlier, in JavaScript code, we made MutationObserver to pass data the object associated with this property.
  3. Load the JavaScript code, the one with the MutationObserver arrangement, and make the Browser execute the code, finishing the wiring between JavaScript and Java.

The remaining step is to load the page into the browser:

        String html = load("index.html");
        browser.loadHTML(html);

Full Java Code

That is it. Here is the complete Java code:

import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import com.teamdev.jxbrowser.chromium.Browser;
import com.teamdev.jxbrowser.chromium.JSObject;
import com.teamdev.jxbrowser.chromium.events.ScriptContextAdapter;
import com.teamdev.jxbrowser.chromium.events.ScriptContextEvent;
import com.teamdev.jxbrowser.chromium.swing.BrowserView;

import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.net.URL;

/**
 * This example shows how to listen to DOM changes from a Java object.
 */
public class ContentListening {

    public static void main(String[] args) {
        Browser browser = new Browser();
        BrowserView view = new BrowserView(browser);

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(view, BorderLayout.CENTER);
        frame.setSize(700, 500);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        browser.addScriptContextListener(new ScriptContextAdapter() {
            @Override
            public void onScriptContextCreated(ScriptContextEvent event) {
                Browser browser = event.getBrowser();
                JSObject window = browser.executeJavaScriptAndReturnValue("window")
                                         .asObject();
                window.setProperty("java", new JavaObject());
                String javaScript = load("observer.js");
                browser.executeJavaScript(javaScript);
            }
        });

        String html = load("index.html");
        browser.loadHTML(html);
    }

    /**
     * The object observing DOM changes.
     *
     * <p>The class and methods that are invoked from JavaScript code must be public.
     */
    public static class JavaObject {

        @SuppressWarnings("unused") // invoked by callback processing code.
        public void onDomChanged(String innerHtml) {
            System.out.println("DOM node changed: " + innerHtml);
        }
    }

    /**
     * Loads a resource content as a string.
     */
    private static String load(String resourceFile) {
        URL url = Resources.getResource(resourceFile);
        String result;
        try {
            result = Resources.toString(url, Charsets.UTF_8);
        } catch (IOException e) {
            throw new IllegalStateException("Unable to load resource " + resourceFile, e);
        }
        return result;
    }
}

If you run this program, you should see the browser window with the counter and the console output following the change in the browser window.

Summary

In this tutorial we create a small Java application that listens to DOM changes in a loaded web page.

The application consists of the following parts:

  • The HTML page with a DOM element which is going to change, and for which we know the ID.
  • The JavaScript code, which uses MutationObserver for notifying a Java object associated with the window object.
  • The Java object that listens to DOM events.
  • The Java code that adds a ScriptContextListener to a Browser instance, loads the web page, and makes the Browser instance execute the JavaScript code that makes the MutationObserver pass changes to the listening Java object.
Go Top