Accessing JavaScript Object

EO.WebBrowser.WebView allows you to access JavaScript object or invoke JavaScript code from your .NET code. This section covers the following topics:

Executing JavaScript Synchronously

The easiest way to execute JavaScript code is to call WebView.EvalScript method. For example, you can use the following code to display a JavaScript alert dialog:

//Execute JavaScript code alert('hi');
webView1.EvalScript("alert('hi');");

If the code evaluates to a value, the value is returned. For example, the following code can be used to get the current page Url:

//Return the current page Url
string url = (string)webView1.EvalScript("document.URL");

When the return value is a simple value such as a string, number, or a date time value, the corresponding .NET type is used. For example, document.URL returns a string, thus EvalScript in the above sample returns a String object.

When the return value is a JavaScript object or function, a JSObject object is returned. For example, EvalScript("window") will return a JSObject that represents the JavaScript window object (you can also use GetDOMWindow method to return the JavaScript window object).

You can use JSObject's Item (indexer for C#) to access a property of the underlying JavaScript object. For example, the following code access the document object's "URL" property:

//Get the JavaScript document object
JSObject document = (JSObject)WebView1.EvalScript("document");

//Get the document object's URL property
string url = (string)document["URL"];

A JSObject can be a JSFunction, which is used to represent a JavaScript function. You can call the JSFunction's Invoke method to invoke the JavaScript function:

//Get the JavaScript window object
JSObject window = webView1.GetDOMWindow();

//Get the alert JavaScript function
JSFunction alert = (JSFunction)WebView1.EvalScript("alert");

//Call the function
alert.Invoke(window, new object[]{"Hi"});

Note the first argument of the Invoke is "this" object for the function, in this case it is the JavaScript window object.

Executing JavaScript Asynchronously

At certain moments you can not call WebView.EvalScript. These moments include:

  • When the WebView has just been created and has not yet ready its internal JavaScript context. If you call EvalScript at this moment, the call will fail with "script context not ready" error;
  • When the JavaScript context is to be re-entered. For example, if your JavaScript code calls a .NET function through a JavaScript extension, then you try to call EvalScript in your .NET function, then it would cause JavaScript context to be re-entered. This is not allowed and the call will fail;
  • Inside the event handler of certain WebView events. Calling EvalScript is not allowed in these handlers because it can potentially causes deadlock in the browser engine;

If you need to execute JavaScript code during any of the above moments, you can use QueueScriptTask. This function will execute the JavaScript code immediately if it can. If it can not due to any of the above conditions, it will execute the JavaScript code later as soon as those conditions clear. After the script has been executed, it will raise ScriptTaskDone event. The result value of the script code is stored in ScriptTask.Result property.

The following code demonstrates how to use QueueScriptTask:

var scriptTask = webView.QueueScriptTask("window.location.href");
scriptTask.OnDone(() =>
{
    var url = scriptTask.Result as string;
    System.Diagnostics.Debug.WriteLine("Page Url = " + url);
});

Returning Value from JavaScript

As demonstrated above, both synchronous and asynchronous JavaScript call returns the result value to you. For synchronous calls, the result value is returned to you directly as the return value of the WebView.EvalScript function. For asynchronous calls, the result value is in ScriptTask.Result. The value can be a simple value such as a number or string, or complex value such as a JavaScript object or function.

Using EvalScript for Advanced Features

Since EvalScript can be used to call any JavaScript code and return values from JavaScript to your .NET code, essentially all features that are available to JavaScript can be make available to .NET by calling EvalScript. For example, the following code returns the current value of a DIV's "display" CSS attribute:

//Get div1's style.display property
string display = (string)webView1.EvalScript(@"
    var div = document.getElementById('div1');
    div.style.display;");

Note that the last line of the code is just "div.style.display", not "return div.style.display" as you would normally write in a function. This is because EvalScript evaluates the script in the page's global context, not inside a local function context. The EvalScript call returns the value of the last statement in the JavaScript code you passed in. In this case it would be the value of "div.style.display".

It is important that the type of the value returned from the JavaScript code and the corresponding .NET side variable type match. EvalScript automatically converts primitive JavaScript types into their corresponding .NET types. For example, "style.display" in the above JavaScript evaluates to a JavaScript string, thus when this value is returned by EvalScript to the .NET code, it is automatically converted into a .NET string, this makes it possible to cast the return value to a string in your .NET code.

For non-primitive JavaScript types, EvalScript returns a JSObject. Note that the actual value returned maybe of an type that derives from JSObject type. For example, a JavaScript function object is returned as a JSFunction object on the .NET side, which derives from JSObject.

It is recommended to use EvalScript instead of classes in EO.WebBrowser.DOM namespace for performance critical code. For example, the following code:

//Get the current document title
string title = webView1.GetDOMWindow().document.title;

Can be rewritten as:

//Get the current document title
string title = webView1.EvalScript("document.title");

This is because the above code only makes a single round trip between the ".NET World" and "JavaScript World", while the previous code using classes in EO.WebBrowser.DOM namespace makes three round trips: first to get the DOM window, then to get the DOM document, then to get the title property of the DOM document.

Because of the performance advantage and ability to practically do almost anything possible with JavaScript, it is recommended to follow these two basic steps when looking for an advanced feature that are not directly exposed by EO.WebBrowser:

  1. Find out the correct JavaScript code to perform the function;
  2. Call EvalScript with the correct JavaScript code;