JavaScript

本指南描述了如何在加载的网页上访问 JavaScript、执行 JavaScript 代码、注入 .NET 对象以从 JavaScript 调用 .NET 以及其他功能。

执行 JavaScript

DotNetBrowser 允许在加载的网页上访问和执行 JavaScript 代码。

要访问 JavaScript,请确保该网页已完全加载且已启用 JavaScript。

要执行 JavaScript 代码,请使用 IFrame.ExecuteJavaScript(string) 方法。 该方法返回一个 Task ,可用于获取执行结果。 获取 Task.Result 属性或调用 Task.Wait() 方法会阻塞当前线程的执行,直到 JavaScript 执行完成。

默认情况下,该方法返回一个表示执行结果的 Task<object> 。 如果 JavaScript 返回 nullundefined,则结果为 null 。 如果存在类型不匹配,此方法的泛型重载会执行直接转换并抛出 ClassCastException。 如果您需要避免这种情况,请将非泛型重载与安全转换结合使用,即 as 运算符

下面的示例执行返回 document 标题的 JavaScript 代码:

string title = frame.ExecuteJavaScript<string>("document.title").Result;
Dim title As String = frame.ExecuteJavaScript(Of String)("document.title").Result

您可以执行任何 JavaScript 代码:

double number = frame.ExecuteJavaScript<double>("123").Result;
bool boolean = frame.ExecuteJavaScript<bool>("true").Result;
string str = frame.ExecuteJavaScript<string>("'Hello'").Result;
IJsObject window = frame.ExecuteJavaScript<IJsObject>("window").Result;
IElement body = frame.ExecuteJavaScript<IElement>("document.body").Result;
Dim number As Double = frame.ExecuteJavaScript(Of Double)("123").Result
Dim [boolean] As Boolean = frame.ExecuteJavaScript(Of Boolean)("true").Result
Dim str As String = frame.ExecuteJavaScript(Of String)("'Hello'").Result
Dim window As IJsObject = frame.ExecuteJavaScript(Of IJsObject)("window").Result
Dim body As IElement = frame.ExecuteJavaScript(Of IElement)("document.body").Result

注入 JavaScript

您可以使用 IBrowser.InjectJsHandler 属性在网页上注入自定义 JavaScript 代码。 此处理程序旨在注入:

  • 将 .NET 对象转换为 JavaScript 代码;
  • 在特定框架中执行任何脚本之前,进一步执行自定义 JavaScript 代码。

请参阅以下代码示例:

browser.InjectJsHandler = new Handler<InjectJsParameters>(args =>
{
    IJsObject window = args.Frame.ExecuteJavaScript<IJsObject>("window").Result;
    window.Properties["myObject"] = new MyObject();
});
browser.InjectJsHandler = New Handler(Of InjectJsParameters)(Sub(args)
    Dim window As IJsObject = args.Frame.ExecuteJavaScript(Of IJsObject)("window").Result
    window.Properties("myObject") = New MyObject()
End Sub)

MyObject 类可能如下所示:

public class MyObject
{
    public string SayHelloTo(string firstName) => "Hello " + firstName + "!";
}
Public Class MyObject
    Public Function SayHelloTo(ByVal firstName As String) As String
        Return "Hello " & firstName & "!"
    End Function
End Class

属性设置完成后,您就可以从 JavaScript 中调用注入的 .NET 对象的方法:

window.myObject.SayHelloTo('John');

对于同一个框架,这个处理程序可能会被多次调用。

避免执行修改正在加载的网页的 DOM 树的 JavaScript 代码。 不得使用 DotNetBrowser DOM API 来移除调用此处理程序的框架,否则会导致渲染过程崩溃。

类型转换

JavaScript 和 .NET 使用不同的原始类型。 DotNetBrowser 实现了从 JavaScript 到 .NET 类型的自动类型转换,反之亦然。

JavaScript 到 .NET

以下规则用于将 JavaScript 转换为 .NET 类型:

  • JavaScript 数字转换为 System.Double
  • JavaScript string - 转换为 System.String
  • JavaScript boolean - 转换为 System.Boolean
  • JavaScript nullundefined - 转换为 null
  • JavaScript 对象被包装为 IJsObject
  • JavaScript DOM 节点对象被包装为 IJsObjectIEventTarget

在上面的代码示例中,我们知道 document.title 是一个字符串,所以我们使用带有 string 类型参数的泛型方法重载。

.NET 到 JavaScript

以下规则用于将 .NET 转换为 JavaScript 类型:

  • System.Double - 转换为 JavaScript Number
  • System.String - 转换为 JavaScript string
  • System.Boolean - 转换为 JavaScript boolean;
  • .NET null - 转换为 JavaScript null;
  • IJsObject - 转换为一个适当的JavaScript 对象。
  • IEventTarget - 转换为一个适当的 JavaScript DOM Node 对象;
  • System.Object 将被包装成一个 JavaScript 对象。

DOM 包装器

根据自动类型转换的规则,JavaScript DOM 对象被包装为 IJsObjectIEventTarget。 您可以使用 DotNetBrowser DOM API 处理 JavaScript DOM 对象。

在下面的代码示例中,我们返回代表 JavaScript DOM 对象的 document。 在这种情况下,可以将返回值类型设置为 IJsObjectIDocument

IDocument document = frame.ExecuteJavaScript<IDocument>("document").Result;
Dim document As IDocument = frame.ExecuteJavaScript(Of IDocument)("document").Result
IJsObject document = frame.ExecuteJavaScript<IJsObject>("document").Result;
Dim document As IJsObject = frame.ExecuteJavaScript(Of IJsObject)("document").Result

使用 JsObject

要使用 .NET 代码中的 JavaScript 对象,请使用 IJsObject 接口。 该接口允许使用对象属性并调用其函数。

属性

要获取 JavaScript 对象的属性名称,包括原型对象中的属性,请使用 IJsObjectPropertyCollection.Names` 属性:

IEnumerable<string> propertyNames = jsObject.Properties.Names;
Dim propertyNames As IEnumerable(Of String) = jsObject.Properties.Names

要检查 JavaScript 对象是否具有指定的属性,请使用 IJsObjectPropertyCollection.Contains(string) 方法:

bool has = jsObject.Properties.Contains("<property-name>");
Dim has As Boolean = jsObject.Properties.Contains("<property-name>")

要通过名称获取 JavaScript 对象属性的值,请使用 Properties[string]。 请参阅以下代码示例: 请参阅以下代码示例:

IJsObject document = frame.ExecuteJavaScript<IJsObject>("document").Result;
object title = document.Properties["title"];
Dim document As IJsObject = frame.ExecuteJavaScript(Of IJsObject)("document").Result
Dim title As Object = document.Properties("title")

Properties[string] 也可用于设置 JavaScript 对象的属性。 例如:

document.Properties["title"] = "New Title";
document.Properties("title") = "New Title"

返回值是可以设置为所需类型的 System.Object。 有关详细信息,请参阅类型转换 部分。

您可以使用以下方法删除属性:

bool success = jsObject.Properties.Remove("<property-name>");
Dim success As Boolean = jsObject.Properties.Remove("<property-name>")

功能

要调用具有所需名称和参数的函数,请使用 Invoke(string methodName, object... args) 方法。 下面的代码示例演示了如何调用 document.getElementById() JavaScript 函数:

IJsObject element = document.Invoke("getElementById", "demo");
Dim element As IJsObject = document.Invoke("getElementById", "demo")

…相当于 JavaScript 中的以下代码:

var element = document.getElementById("demo");

如果在函数执行期间发生错误,该方法将抛出 JsException

动态类型

您可以使用以下方式调用 JavaScript 对象:

dynamic document = Browser.MainFrame.ExecuteJavaScript("document").Result;
document.write("Hello world!");
Dim document As Object = Browser.MainFrame.ExecuteJavaScript("document").Result
document.write("Hello world!")

下面是一个更复杂的示例,演示如何使用 动态类型 后期绑定来使用 XMLHttpRequest 和发送 AJAX 请求:

dynamic xhr = browser.MainFrame.ExecuteJavaScript("new XMLHttpRequest()").Result;
var url = "https://jsonplaceholder.typicode.com/todos/1";
xhr.open("GET", url, true);

// 在 readystatechange 上执行函数。
xhr.onreadystatechange = (Action<dynamic>)((o) =>
{
    if (xhr.readyState == 4 && xhr.status == 200)
    {
        System.Console.WriteLine(xhr.responseText);
    }
});
// 发送我们的请求。
xhr.send();

// 在主线程中等待以查看打印出的响应。
Thread.Sleep(10000);
Dim xhr As Object
xhr = browser.MainFrame.ExecuteJavaScript("new XMLHttpRequest()").Result
Dim url = "https://jsonplaceholder.typicode.com/todos/1"
xhr.open("GET", url, true)

' 在 readystatechange 上执行函数。
xhr.onreadystatechange = Sub (o As Object)
    If xhr.readyState = 4 And xhr.status = 200
        System.Console.WriteLine(xhr.responseText.ToString())
    End If
End Sub
' 发送我们的请求。
xhr.send()

' 在主线程中等待以查看打印出的响应。
Thread.Sleep(10000)

使用 JavaScript 函数

从 DotNetBrowser 2.1开始, 您可以直接从 .NET 代码使用 JavaScript 函数,并将对函数的引用从 JavaScript 传递到 .NET。 例如:

IJsFunction alert = frame.ExecuteJavaScript<IJsFunction>("window.alert").Result;
alert?.Invoke(window, "Hello world!");
Dim alert As IJsFunction = frame.ExecuteJavaScript(Of IJsFunction)("window.alert").Result
alert?.Invoke(window, "Hello world!")

使用 JavaScript Promise

从 DotNetBrowser 2.7 开始,您可以直接使用 JavaScript Promise,而无需在 IJsObject 上创建包装器。 例如:

IJsPromise promise = browser.MainFrame
                            .ExecuteJavaScript<IJsPromise>("Promise.resolve(\"test\")")
                            .Result;

// 当 promise 被履行或拒绝时调用一些 .NET 代码。
promise.Then(o => Console.WriteLine("Promise fulfilled"),
             e => Console.WriteLine("Promise rejected"));
Dim promise As IJsPromise =
    browser1.MainFrame.ExecuteJavaScript(
        Of IJsPromise)("Promise.resolve(""test"")").Result

' 当 promise 被履行或拒绝时调用一些 .NET 代码。
promise.Then(Sub(o) Console.WriteLine("Promise fulfilled"), 
             Sub(e) Console.WriteLine("Promise rejected"))

从 JavaScript 调用 .NET

当您在调用 JavaScript 函数时将 System.Object 作为属性值或参数传递时,.NET 对象将自动转换或包装为 JavaScript 对象。 它允许将 .NET 对象注入 JavaScript 并从 JavaScript 代码调用其公共方法。

以下代码示例演示了如何将 .NET 对象作为属性值传递,并从 JavaScript 调用其公共方法:

public class MyObject {

    public string SayHelloTo(string firstName) {
        return "Hello " + firstName + "!";
    }
Public Class MyObject
    Public Function SayHelloTo(ByVal firstName As String) As String
        Return "Hello " & firstName & "!"
    End Function
End Class
IJsObject window = frame.ExecuteJavaScript<IJsObject>("window").Result;
window.Properties["MyObject"] = new MyObject();
Dim window As IJsObject = frame.ExecuteJavaScript(Of IJsObject)("window").Result
window.Properties("MyObject") = New MyObject()

之后,您可以在 JavaScript 代码中引用该对象并调用其方法:

console.log(window.MyObject.SayHelloTo("John"));

控制台输出如下:

Hello John!

类型转换

当从 JavaScript 调用注入的 .NET 对象的公共方法时,JavaScript-.NET 桥提供自动类型转换功能。

如果可能,该库会自动将给定的 JavaScript Number 转换为所需的 .NET 类型。 如果我们检测到给定的数字无法在不丢失数据的情况下转换为例如.NET byte,则库会抛出异常并通知 JavaScript 没有合适的 .NET 方法。 如果给定值可以在不丢失数据的情况下转换,则库会对其进行转换并调用相应的 .NET 方法。

例如,如果将以下 .NET 对象注入 JavaScript:

public sealed class MyObject {

    public int Method(int intValue) {
        return intValue;
    }
}
Public NotInheritable Class MyObject
    Public Function Method(ByVal intValue As Integer) As Integer
        Return intValue
    End Function
End Class

之后,您可以从 JavaScript 中调用它,并传递能够在不数据丢失的情况下转换为 Integer 的 JavaScript Number 值:

window.MyObject.method(123);

但是,如果您传递的 Double 值不能在不丢失数据的情况下转换为 Integer ,则会得到以下错误:

window.MyObject.method(3.14); // <- 错误

注入委托

从 JavaScript 调用 .NET 的另一种方法是注入委托。

JavaScript-.NET 桥允许您将委托与 JavaScript 属性相关联。 这样的委托将被视为可以在 JavaScript 代码中调用的函数。

例如,您可以使用以下代码注册与委托关联的 JavaScript 函数:

IJsObject window = frame.ExecuteJavaScript<IJsObject>("window").Result;
if (window != null) {
    window.Properties["sayHello"] = (Func<string, string>) ((args) => "Hello, " + args[0]);
}
Dim window As IJsObject = frame.ExecuteJavaScript(Of IJsObject)("window").Result
If window IsNot Nothing Then
    window.Properties("sayHello") = CType(Function(args) "Hello, " & args(0), 
        Func(Of String, String))
End If

现在,您可以通过以下方式在 JavaScript 中调用此函数:

window.sayHello('John');

类型转换规则不适用于委托。 在这种情况下,JavaScript Number 总是被解释为 Double

控制台消息

DotNetBrowser 允许使用 JavaScript 的 console.log() 函数接收发送到控制台的所有输出消息。 您可以_listen_以下级别的消息:

  • DEBUG
  • LOG
  • WARNING
  • ERROR

要获得控制台收到消息的通知,请使用 ConsoleMessageReceived 事件。 请参阅以下代码示例:

browser.ConsoleMessageReceived += (s, e) => {
    string consoleMessage = e.Message;
    ConsoleMessageReceivedEventArgs.MessageLevel level = e.Level;
};
AddHandler browser.ConsoleMessageReceived, Sub(s, e)
    Dim consoleMessage As String = e.Message
    Dim level As ConsoleMessageReceivedEventArgs.MessageLevel = e.Level
End Sub

使用 JSON

DotNetBrowser JS - .NET Bridge API 允许将表示 JSON 的字符串转换为一个或多个 JavaScript 对象。 下面的代码示例演示了如何从 JSON 字符串创建 JavaScript 对象:

string jsonString = "[123, \"Hello\"]";
IJsObject jsObject = browser.MainFrame.ParseJsonString<IJsObject>(jsonString);
Dim jsonString As String = "[123, ""Hello""]"
Dim jsObject As IJsObject = browser.MainFrame.ParseJsonString(Of IJsObject)(jsonString)

如果您尝试解析不代表有效 JSON 的字符串值,则会抛出 JsException

在上面的代码示例中, "[123, \"Hello\"]" JSON 字符串被转换为一个适当的 JavaScript 对象。 在这种情况下,它将被转换为一个数组。

可以使用 ToJsonString() 扩展方法将 IJsObject 转换为 JSON 字符串。 它会返回一个相当于 JavaScript 的 JSON.stringify() 方法的字符串:

string jsonRepresentation = jsObject.ToJsonString();
Dim jsonRepresentation As String = jsObject.ToJsonString()
Go Top