DOM
SWT
Integração
Registro de logs
IDE
Ouvir as alterações de conteúdo do DOM
Este tutorial mostra como criar um pequeno aplicativo Java que escuta alterações ocorridas em uma página HTML carregada no JxBrowser.
Pré-requisitos
Para realizar este tutorial, você vai precisar de:
- Git
- Java 8 ou superior
- Uma licença válida do JxBrowser. Pode ser de avaliação ou comercial. Para mais informações sobre o licenciamento, consulte o guia Licenciamento.
Configurando um projeto
O código do aplicativo de exemplo para este tutorial está disponível junto com outros exemplos de um repositório do GitHub como um projeto baseado no Gradle.
Se você deseja construir um projeto baseado em Maven, consulte o guia Configuração Maven. Se quiser construir um projeto baseado no Gradle a partir do zero, consulte o guia Configuração Gradle.
Obtendo o código
Para obter o código, execute os seguintes comandos:
$ git clone https://github.com/TeamDev-IP/JxBrowser-Examples
$ cd JxBrowser-Examples/tutorials/content-changes
Agora, estamos no diretório raiz de todos os exemplos. O código deste tutorial está sob o diretório
tutorials/content-changes
.
Adicionar a licença
Para executar este tutorial, é necessário configurar uma chave de licença.
A página
Vamos carregar uma página HTML simples na qual mostraremos um contador que se incrementa automaticamente a cada segundo.
Aqui está o código do contador:
<div>
<span class="counter" id="counter"></span>
</div>
O contador é atualizado utilizando jQuery:
<script crossorigin="anonymous" src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var counter = 1;
setInterval(function() { $(".counter").text(counter++); }, 1000);
});
</script>
Aqui está o código completo da página, que está incluído no projeto como o arquivo de recurso
chamado 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 crossorigin="anonymous" src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var counter = 1;
setInterval(function() { $(".counter").text(counter++); }, 1000);
});
</script>
</body>
</html>
O código JavaScript
Como queremos notificar o código Java da nossa aplicação a partir do nosso código HTML, precisamos do código JavaScript que trata das modificações do DOM e as transmite ao nosso código Java. Podíamos tê-lo feito diretamente no código HTML, mas queremos mostrar como adicionar esse código dinamicamente a partir de Java.
Primeiro, obtemos o elemento a seguir:
const element = document.getElementById('counter');
Em seguida, criamos uma instância MutationObserver
com um retorno de chamada para passar os dados para o código Java.
const observer = new MutationObserver(
function(mutations) {
window.java.onDomChanged(element.innerHTML);
});
A parte mais importante aqui é esta chamada:
window.java.onDomChanged(element.innerHTML);
Aqui fazemos referência a um objeto armazenado no objeto window
, uma vez que a propriedade denominada java
é o nome da propriedade que contém o objeto Java a ser chamado.
Utilizamos a palavra java
para realçar o fato de estarmos chamando um objeto Java.
Pode ser qualquer identificador JavaScript que corresponda ao sentido da sua aplicação.
O método do objeto que estamos chamando é onDomChanged()
. Mais tarde, adicionaremos este método quando
criarmos uma classe Java para ouvir as alterações de conteúdo.
Passamos a propriedade innerHTML
do elemento contador. Assim, o método aceitará um parâmetro String
.
Agora, vamos dizer ao observer
para acompanhar as mudanças no DOM:
const config = {childList: true};
observer.observe(element, config);
Aqui está o código JavaScript completo que colocamos nos recursos como arquivo observer.js
:
const element = document.getElementById('counter');
const observer = new MutationObserver(
function(mutations) {
window.java.onDomChanged(element.innerHTML);
});
const config = {childList: true};
observer.observe(element, config);
O código Java
Utilitário para carregar recursos
Nas seções anteriores, analisamos o código HTML e JavaScript armazenado como recursos.
Agora, precisamos do código que os carrega. Vamos utilizar a abordagem mais simples utilizando o utilitário Resources
da classe
do Guava.
Aqui está o código do método utilitário load()
que usaremos mais tarde:
private static String load(String resourceFile) {
URL url = ContentListening.class.getResource(resourceFile);
try (Scanner scanner = new Scanner(url.openStream(),
Charsets.UTF_8.toString())) {
scanner.useDelimiter("\\A");
return scanner.hasNext() ? scanner.next() : "";
} catch (IOException e) {
throw new IllegalStateException("Unable to load resource " +
resourceFile, e);
}
}
Criação do Browser e do BrowserView
Para simplificar o exemplo, vamos colocar todo o código no método main()
.
Uma aplicação real teria um código mais estruturado.
Antes de mais nada, precisamos criar um Engine
e um Browser
:
Engine engine = Engine.newInstance(
EngineOptions.newBuilder(HARDWARE_ACCELERATED).build());
Browser browser = engine.newBrowser();
Em seguida, no Swing EDT, criamos um BrowserView
e um JFrame
. Em seguida, adicionamos o recém-criado BrowserView
ao frame.
SwingUtilities.invokeLater(() -> {
BrowserView view = BrowserView.newInstance(browser);
JFrame frame = new JFrame("Content Listening");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(view, BorderLayout.CENTER);
frame.setSize(700, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
Agora, precisamos criar um objeto que ouça as alterações do DOM.
Objeto Java para ouvir alterações no DOM
Como você deve se lembrar, o código JavaScript que revisamos anteriormente precisava de um objeto com o método chamado
onDomChanged()
que aceita o argumento String
.
Aqui está a classe:
public static class JavaObject {
@SuppressWarnings("unused") // Invoked by the callback processing code.
@JsAccessible
public void onDomChanged(String innerHtml) {
System.out.println("DOM node changed: " + innerHtml);
}
}
Agora, precisamos criar código JavaScript para conversar com o nosso objeto Java. Vamos fazer isso.
Conectando JavaScript e Java
Para que o código JavaScript converse com nosso objeto Java,
implementaremos um InjectJsCallback
passando a instância para Browser.set()
.
browser.set(InjectJsCallback.class, params -> {
Frame frame = params.frame();
String window = "window";
JsObject jsObject = frame.executeJavaScript(window);
if (jsObject == null) {
throw new IllegalStateException(
format("'%s' JS object not found", window));
}
jsObject.putProperty("java", new JavaObject());
return Response.proceed();
});
Neste código, nós:
1. Obtemos uma instância do objeto JavaScript `window`.
2. Criamos um objeto Java que escuta as alterações de conteúdo e o define como uma propriedade chamada `java`
na `window`. Anteriormente, no código JavaScript, fizemos `MutationObserver` para passar dados
o objeto associado a esta propriedade.
Em seguida, devemos registrar o evento listener FrameLoadFinished
que carregará o código JavaScript, o
com o arranjo MutationObserver
, e fará com que o Browser
execute o código, finalizando a ligação
entre JavaScript e Java quando o modelo DOM estiver pronto.
browser.navigation().on(FrameLoadFinished.class, event -> {
String javaScript = load("observer.js");
event.frame().executeJavaScript(javaScript);
});
O passo seguinte é carregar a página no browser:
String html = load("index.html");
String base64Html = Base64.getEncoder().encodeToString(html.getBytes(UTF_8));
String dataUrl = "data:text/html;base64," + base64Html;
browser.navigation().loadUrl(dataUrl);
Código Java completo
É isso. Aqui está o código Java completo:
import static com.teamdev.jxbrowser.engine.RenderingMode.HARDWARE_ACCELERATED;
import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Charsets;
import com.teamdev.jxbrowser.browser.Browser;
import com.teamdev.jxbrowser.browser.callback.InjectJsCallback;
import com.teamdev.jxbrowser.browser.callback.InjectJsCallback.Response;
import com.teamdev.jxbrowser.engine.Engine;
import com.teamdev.jxbrowser.engine.EngineOptions;
import com.teamdev.jxbrowser.frame.Frame;
import com.teamdev.jxbrowser.js.JsAccessible;
import com.teamdev.jxbrowser.js.JsObject;
import com.teamdev.jxbrowser.navigation.event.FrameLoadFinished;
import com.teamdev.jxbrowser.view.swing.BrowserView;
import java.awt.BorderLayout;
import java.io.IOException;
import java.net.URL;
import java.util.Base64;
import java.util.Scanner;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
/**
* Este exemplo demonstra como ouvir alterações DOM a partir de um objeto Java.
*/
public final class ContentListening {
public static void main(String[] args) {
Engine engine = Engine.newInstance(
EngineOptions.newBuilder(HARDWARE_ACCELERATED).build());
Browser browser = engine.newBrowser();
SwingUtilities.invokeLater(() -> {
BrowserView view = BrowserView.newInstance(browser);
JFrame frame = new JFrame("Content Listening");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(view, BorderLayout.CENTER);
frame.setSize(700, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
browser.set(InjectJsCallback.class, params -> {
Frame frame = params.frame();
String window = "window";
JsObject jsObject = frame.executeJavaScript(window);
if (jsObject == null) {
throw new IllegalStateException(
format("'%s' JS object not found", window));
}
jsObject.putProperty("java", new JavaObject());
return Response.proceed();
});
browser.navigation().on(FrameLoadFinished.class, event -> {
String javaScript = load("observer.js");
event.frame().executeJavaScript(javaScript);
});
String html = load("index.html");
String base64Html = Base64.getEncoder().encodeToString(html.getBytes(UTF_8));
String dataUrl = "data:text/html;base64," + base64Html;
browser.navigation().loadUrl(dataUrl);
}
/**
* Carrega o conteúdo de um recurso como uma cadeia de caracteres.
*/
private static String load(String resourceFile) {
URL url = ContentListening.class.getResource(resourceFile);
try (Scanner scanner = new Scanner(url.openStream(),
Charsets.UTF_8.toString())) {
scanner.useDelimiter("\\A");
return scanner.hasNext() ? scanner.next() : "";
} catch (IOException e) {
throw new IllegalStateException("Unable to load resource " +
resourceFile, e);
}
}
/**
* O objeto que observa as alterações do DOM.
*
* <p>A classe e os métodos que são invocados a partir do código JavaScript devem ser públicos.
*/
public static class JavaObject {
@SuppressWarnings("unused") // invoked by callback processing code.
@JsAccessible
public void onDomChanged(String innerHtml) {
System.out.println("DOM node changed: " + innerHtml);
}
}
}
Se você executar este programa, deverá ver a janela do browser com o contador e a saída da console seguindo alteração na janela do browser.
Resumo
Neste tutorial, criamos um pequeno aplicativo Java que escuta as alterações do DOM numa página carregada da Web.
A aplicação é composta pelas seguintes partes:
- A página HTML com um elemento DOM que vai ser alterado e para o qual sabemos o ID.
- O código JavaScript, que utiliza
MutationObserver
para notificar um objeto Java associado com o objetowindow
. - O objeto Java que ouve os eventos DOM.
- O código Java que adiciona um
InjectJsCallback
a uma instânciaBrowser
, carrega a página web e faz com que a instânciaBrowser
execute o código JavaScript que faz com que oMutationObserver
passe as alterações para o objeto Java que está a ouvir.