DOM

本指南介绍如何访问 DOM 文档、查找元素、修改 DOM 结构、模拟用户输入等操作。

概述 

每个在 Browser 中加载的网页都有一个主 FrameFrame 本身也可能包含子 frame。比如,当网页包含 IFRAME 时,请使用 Frame 类来访问 DOM 和 JavaScript。

访问文档 

每个 Frame 都有一个 DOM Document。要访问 Document,请使用 Frame.document() 方法:

Java
Kotlin
frame.document().ifPresent(document -> {
});
val document = frame.document
document?.let {
}

查找元素 

你可以按不同条件在某个元素内部查找 HTML 元素。下面示例演示如何在文档元素中查找所有 DIV 元素:

Java
Kotlin
document.documentElement().ifPresent(documentElement ->
        documentElement.findElementsByTagName("div")
                .forEach(element -> {
                    // ...
                })
);
val divs = document.documentElement!!.findElementsByTagName("div")
divs.forEach { element -> }

如果你只需要找到第一个 HTML 元素,可以使用以下方式:

Java
Kotlin
documentElement.findElementByTagName("div").ifPresent(element -> {
});
val div = documentElement.findByTagName("div")
div?.let {
}

下面是按不同条件搜索 HTML 元素的示例:

Java
Kotlin
documentElement.findElementsById("<id>");
documentElement.findElementsByName("<attr-name>");
documentElement.findElementsByTagName("<tag-name>");
documentElement.findElementsByClassName("<attr-class>");
documentElement.findElementsById("<id>")
documentElement.findElementsByName("<attr-name>")
documentElement.findElementsByTagName("<tag-name>")
documentElement.findElementsByClassName("<attr-class>")

XPath 

Fuzio 的 DOM API 支持使用 Node.evaluate(String expression) 来计算 XPath 表达式。你可以在指定 Node 的作用域内通过以下代码计算 XPath 表达式:

Java
Kotlin
try {
    var result = node.evaluate("count(//div)");
} catch (XPathException e) {
    // 计算给定表达式失败。
}
val queryNumberOfDivs = XPathExpression("count(//div)")
try {
    val result: XPathResult = node.evaluate(queryNumberOfDivs)
} catch (e: XPathException) {
    // 计算给定表达式失败。
}

当库无法计算给定表达式时,该方法会抛出 XPathException

计算结果保存在 XPathResult 对象中。请确保结果包含你期望的值类型(例如 NumberBooleanStringNode),并提取实际值:

Java
Kotlin
if (result.isNumber()) {
    double number = result.asNumber();
}
if (result.isNumber) {
    val number: Double = result.asNumber()
}

查询选择器 

要查找匹配指定选择器(例如 #root)的元素,请使用以下代码:

Java
Kotlin
var elements = element.findElementsByCssSelector("#root");
val elements = element.findElementsByCssSelector("#root")

坐标点处的节点 

要查找网页上某个指定点(例如 100x150)处的 Node,请使用以下方式:

Java
Kotlin
var inspection = frame.inspect(Point.of(100, 150));
inspection.node().ifPresent(node -> {
});
val inspection: PointInspection = frame.inspect(Point(100, 150))
val node: Node? = inspection.node

操作元素 

元素边界 

你可以获取 Element 的边界矩形(相对于当前 Document 视口左上角的位置),如下所示:

Java
Kotlin
var rect = element.boundingClientRect();
val rect = element.boundingClientRect()

如果元素带有 hidden 属性,或者该元素的 CSS 样式包含 display: none;,此方法将返回一个空的 Rect

元素属性 

Element 类提供了用于获取、添加、删除或修改 HTML 元素属性的方法。下面示例演示如何获取元素的所有属性,并打印每个属性的名称和值:

Java
Kotlin
element.attributes().asMap().forEach((name, value) ->
        System.out.println(name + ": " + value));
element.attributes.asMap().forEach { (name, value) ->
    println("$name: $value")
}

下面示例演示如何添加/修改元素属性:

Java
Kotlin
element.attributes().put("attrName", "attrValue");
element.attributes["attrName"] = "attrValue"

元素内容 

Element 类提供了用于获取或修改元素内容的方法。这些方法与 JavaScript 的 HTMLElement 中对应的属性一一映射:

Java
Kotlin
// 获取 innerHTML 属性的值。
var innerHtml = element.innerHtml();
// 设置 innerHTML 属性的值。
element.innerHtml("<span>New inner HTML<span>");

// 获取 outerHTML 属性的值。
var outerHtml = element.outerHtml();
// 设置 outerHTML 属性的值。
element.outerHtml("<div><p>New outer HTML</p></div>");

// 获取 textContent 属性的值。
var textContent = element.textContent();
// 设置 textContent 属性的值。
element.textContent("New text content");

// 获取 innerText 属性的值。
var innerText = element.innerText();
// 设置 innerText 属性的值。
element.innerText("New inner text");
// 获取 innerHTML 属性的值。
val innerHtml = element.innerHtml
// 设置 innerHTML 属性的值。
element.innerHtml("<span>New inner HTML<span>")

// 获取 outerHTML 属性的值。
val outerHtml = element.outerHtml
// 设置 outerHTML 属性的值。
element.outerHtml("<div><p>New outer HTML</p></div>")

// 获取 textContent 属性的值。
val textContent = element.textContent()
// 设置 textContent 属性的值。
element.textContent("New text content")

// 获取 innerText 属性的值。
val innerText = element.innerText
// 设置 innerText 属性的值。
element.innerText("New inner text")

聚焦元素 

如果浏览器已获得焦点,并且该元素是可聚焦的,你可以将焦点设置到该元素上。

Java
Kotlin
element.focus();
element.focus()

调用 focus() 不会将该元素滚动到可见区域,但你可以手动滚动:

Java
Kotlin
element.scrollIntoView(AlignTo.TOP);
element.scrollIntoView(AlignTo.TOP)

相应地,你也可以通过 blur() 移除元素的焦点:

Java
Kotlin
element.blur();
element.blur()

创建元素 

DOM API 支持修改文档的 DOM 结构。下面示例演示如何创建一个 <p> 元素并插入一些文本:

Java
Kotlin
// 创建一个新的段落元素。
var paragraph = document.createElement("p");
// 创建一个带有给定文本的文本节点。
var text = document.createTextNode("Text");
// 将文本节点插入到段落元素中。
if (paragraph.appendChild(text)) {
    // 将段落元素插入到所需的元素中。
    boolean success = element.appendChild(paragraph);
}
// 创建一个新的段落元素。
val paragraph = document.createElement("p")
// 创建一个带有给定文本的文本节点。
val text = document.createTextNode("Text")
// 将文本节点插入到段落元素中。
if (paragraph.appendChild(text)) {
    // 将段落元素插入到所需的元素中。
    val success = element.appendChild(paragraph)
}

关闭节点 

具有 Node 对应对象的 DOM 对象不受 Blink 垃圾回收机制管理。默认情况下,我们会一直将这些对象保留在内存中,直到页面被卸载。

为了优化内存使用,你可以按对象启用垃圾回收:

Java
Kotlin
node.close();
node.close()

关闭 Node 会将对应的 Blink 对象标记为可回收,但不会立即释放对象。调用 close() 后,任何对该 Node 的使用尝试都会导致 ObjectClosedException

DOM 事件 

每个 Node 都实现了 EventTarget 接口,该接口提供用于注册 DOM 事件的方法。你可以注册监听器来接收 clickmousedownmouseupkeydownloaderror 等 DOM 事件。

下面示例演示如何为 document 的 HTML 元素注册 click 事件监听器:

Java
Kotlin
document.documentElement().ifPresent(element ->
        element.addEventListener(EventType.CLICK, event -> {
            // 已收到鼠标点击事件。
            if (event instanceof MouseEvent) {
                var mouseEvent = (MouseEvent) event;
                var clickCount = mouseEvent.clickCount();
            }
        }, false)
);
val listener = Observer<Event> { event ->
    // 已收到鼠标点击事件。
    if (event is MouseEvent) {
        val clickCount = event.clickCount()
    }
}
val useCapture = false
document.documentElement?.addEventListener(
    EventType.CLICK,
    listener,
    useCapture
)

此外,Fuzio 允许您监听自定义 DOM 事件并访问其有效载荷:

Java
Kotlin
// 创建一个自定义 DOM 事件类型。
var eventType = EventType.of("MyEvent");
// 侦听给定事件类型的事件。
element.addEventListener(eventType, event -> {
    // 已收到 MyEvent 事件。
    if (event instanceof CustomEvent) {
        var customEvent = (CustomEvent) event;
        var payload = customEvent.detail();
    }
}, false);
// 创建一个自定义 DOM 事件类型。
val eventType = EventType.of("MyEvent")
val listener = Observer<Event> { event ->
    // 已收到 MyEvent 事件。
    if (event is CustomEvent) {
        val payload = event.detail<JsObject>()
    }
}
val useCapture = false
// 侦听给定事件类型的事件。
element.addEventListener(eventType, listener, useCapture)

自动化 

Fuzio DOM API 提供了自动化网页表单填写所需的一切功能。本节描述如何更新文本字段中的文本、选择复选框或单选按钮、在下拉列表中选择一个或多个选项、模拟点击等。

要处理网页表单控件,请使用 FormControlElement 接口。该接口允许检查控件是否已启用,并修改其值。所有表单控件,如 INPUTSELECTTEXTAREA 等,都继承此接口。

输入 

要处理 INPUT 元素,请使用 InputElement 接口。它提供了检查输入类型和设置其值所需的所有方法。

文本、电子邮件、密码 

要用新值替换文本、电子邮件或密码字段的默认值,请使用 InputElement.value(String) 方法。

例如,如果你的网页表单包含以下类型的 <input> 元素:

<input type="text" id="firstname" placeholder="First Name">
<input type="email" id="email" placeholder="Email Address">
<input type="password" id="password" placeholder="Password">

你可以用以下方式设置它们的值:

Java
Kotlin
documentElement.findElementById("firstname").ifPresent(element ->
        ((InputElement) element).value("John"));

documentElement.findElementById("email").ifPresent(element ->
        ((InputElement) element).value("me@company.com"));

documentElement.findElementById("password").ifPresent(element ->
        ((InputElement) element).value("Jkdl12!"));
val firstname = documentElement.findById("firstname") as InputElement
firstname.value = "John"

val email = documentElement.findById("email") as InputElement
email.value = "me@company.com"

val password = documentElement.findById("password") as InputElement
password.value = "Jkd12!"

复选框、单选框 

要选中单选框或复选框,请使用 InputElement.check() 方法。

例如,如果你的网页表单包含如下 <input> 元素类型:

<input type="checkbox" id="checkbox" value="Remember me">
<input type="radio" id="radio" checked>

你可以用以下方式选中/取消选中它们:

Java
Kotlin
documentElement.findElementById("checkbox").ifPresent(element ->
        ((InputElement) element).check());

documentElement.findElementById("radio").ifPresent(element ->
        ((InputElement) element).uncheck());
val checkbox = documentElement.getById("checkbox") as InputElement
checkbox.check()

val radio = documentElement.getById("radio") as InputElement
radio.uncheck()

文件 

type=file<input> 元素允许用户从设备存储中选择一个或多个文件。Fuzio 允许你以编程方式选择文件并更新 <input type=file> 元素的值。

例如,如果你的网页表单包含如下 <input> 元素:

<input type="file" id="avatar" accept="image/png, image/jpeg" multiple>

你可以用以下方式以编程方式选择所需一个文件或多个文件:

Java
Kotlin
documentElement.findElementById("avatar").ifPresent(element ->
        ((InputElement) element).file("file1.png", "file2.jpeg"));
val avatar = documentElement.getById("avatar") as InputElement
avatar.file("file1.png", "file2.png")

文本区域 

要在如下 <textarea> 元素中设置文本:

<textarea id="details"></textarea>

请使用以下方式:

Java
Kotlin
documentElement.findElementById("details").ifPresent(element ->
        ((TextAreaElement) element).value("Some text...")
);
val details = documentElement.getById("details") as TextAreaElement
details.value = "Some text..."

选择与选项 

要选中如下 SELECT 控件中的所有选项:

<select id="fruits" multiple>
    <option>Apple</option>
    <option>Orange</option>
    <option>Pineapple</option>
    <option>Banana</option>
</select>

请使用以下方式:

Java
Kotlin
documentElement.findElementById("fruits").ifPresent(element -> {
    var selectElement = (SelectElement) element;
    selectElement.options().forEach(optionElement ->
            optionElement.select()
    );
});
val fruits = documentElement.getById("fruits") as SelectElement
fruits.options.forEach {
    it.select()
}

用同样的方式,你也可以只选择某一个所需的选项。

模拟点击 

要模拟对某个元素的鼠标点击,请使用以下方法:

Java
Kotlin
element.click();
element.click()

当对支持的元素(如 <input>)使用 click() 时,它会触发该元素的 click 事件。随后事件会在文档树中向上冒泡,并触发更高层元素的 click 事件。

派发事件 

你可以使用 EventTarget.dispatch(Event) 方法在指定的 EventTarget 上派发一个 Event

下面示例演示如何在指定元素上派发一个标准的 click 事件:

Java
Kotlin
// 客户端坐标和屏幕坐标。
var location = Point.of(10, 10);
// 使用所需参数创建 MouseEvent。
var mouseClickEvent = document.createMouseEvent(EventType.CLICK,
        MouseEventParams.newBuilder()
                // 按下的主鼠标按钮。
                .button(MouseEvent.Button.MAIN)
                .clientLocation(location)
                .screenLocation(location)
                .uiEventModifierParams(
                        UiEventModifierParams.newBuilder()
                                .eventParams(EventParams.newBuilder()
                                        .bubbles(true)
                                        .cancelable(true)
                                        .build()
                                ).build()
                ).build()
);
// 在目标元素上派发点击事件。
element.dispatch(mouseClickEvent);
// 客户端坐标和屏幕坐标。
val location = Point(10, 10)
// 使用所需参数创建 MouseEvent。
val eventParams = EventParams(bubbles = true, cancelable = true)
val uiEventModifiers = UiEventModifierParams(eventParams)
val mouseEventParams = MouseEventParams(
    button = MouseEvent.Button.MAIN, // 按下的主鼠标按钮。
    location = location,
    locationOnScreen = location,
    uiEventModifierParams = uiEventModifiers
)
val mouseClickEvent = document.createMouseEvent(
    EventType.CLICK,
    mouseEventParams
)
// 在目标元素上派发点击事件。
element.dispatch(mouseClickEvent)

使用这种方式,你可以创建并派发各种 DOM 事件。这类事件通常被称为合成事件(synthetic events),以区别于由 Browser 自身触发的事件。

微信咨询

即库客服

微信公众号二维码

技术客服

微信公众号二维码