Introduction

On some occasions when using a TWebBrowser I've needed to use Delphi to call JavaScript functions contained in the current document.

This is quite easy to do. We'll first examine the techniques then we'll look at a case study that changes the font in an HTML document.

Overview of solution

The window object associated with an HTML document exposes a method – execScript – that enables JavaScript to be called. The first parameter of this method takes a string containing the required function call (complete with actual parameters). The method's second parameter specifies the script language being used – in our case 'JavaScript'.

So, how do we get hold of the window object that exposes the required method? We simply use the parentWindow property of the web browser control's document object. We need to ensure that a document object is available by first loading the required document into the web browser and then wait for it to finish loading.

Implementing the solution

Let us assume we have a TWebBrowser control on a form into which an HTML document has been loaded. Assume also that the HTML document defines a JavaScript function named Foo() that takes a string and an integer parameter. Assuming the document is fully loaded, we can call Foo() with a method similar to this:

uses
  MSHTML;
  
procedure TForm1.CallFoo(S: string; I: Integer);
  { Calls JavaScript Foo() function }
var
  Doc: IHTMLDocument2;      // current HTML document
  HTMLWindow: IHTMLWindow2; // parent window of current HTML document
  JSFn: string;             // stores JavaScipt function call
begin
  // Get reference to current document
  Doc := WebBrowser1.Document as IHTMLDocument2;
  if not Assigned(Doc) then
    Exit;
  // Get parent window of current document
  HTMLWindow := Doc.parentWindow;
  if not Assigned(HTMLWindow) then
    Exit;
  // Run JavaScript
  try
    JSFn := Format('Foo("%s",%d)', [S, I]);  // build function call
    HTMLWindow.execScript(JSFn, 'JavaScript'); // execute function
  except
    // handle exception in case JavaScript fails to run
  end;
end;

Listing 1

Let's look at what's going on here. We first check that a document is available and store a reference to in Doc. We then attempt to get a reference to the document's parent window object and store it in HTMLWindow. Once we have a valid window object we call its execScript method, passing the call to Foo(). The parameters to Foo() must be literal values – we can't pass variables. String literals must be enclosed by double or single quotes.

Escaping quotes in string literal parameters

When passing string literal parameters to JavaScript functions you need to be careful to escape any quote characters contained in the string, otherwise the quote will terminate the string prematurely. Since JavaScript can use either single or double quotes to delimit literal strings you may need to escape either of these types of quote by preceeding it with a backslash.

As an example, suppose we need to pass the string He didn't say "hello" to a JavaScript function. If we delimit the string with double quotes we pass the it as "He didn't say "hello"".

Alternatively we may delimit the string with single quotes and pass it as 'He didn't say "hello"'.

If the JavaScript function contains errors or doesn't exist an exception will be raised. We may wish to handle such exceptions before returning.

Note that execScript always returns a varEmpty value so we can't use it to return a value from a JavaScript function. This means we can only use execScript to call JavaScript functions that return no value or where we don't need the return value.

Case study

In this case study we will develop a small application that displays an HTML document in a browser window. The document will contain a Javascript function – SetFont() – that can change document's default font. The application will display a combo box containing a list of all installed screen fonts. When the user selects a font from the combo box the font used in the web browser will be changed accordingly. We do this by calling SetFont() from Delphi.

Firstly, create an HTML document with any content you choose and include the following JavaScript function in the document's <head> section:

<script type="text/javascript">
<!--
  function SetFont(fontname) {
    document.body.style.fontFamily = fontname;
  }
// -->
</script>

Listing 2

This is the code that changes the font.

Now create a new delphi application and drop a TComboBox and a TWebBrowser on the main form. We will load the HTML document when the form is first shown. We will also load the screen fonts into the combo box at the same time. To do this create an OnShow event handler for the form with the following code:

procedure TForm1.FormShow(Sender: TObject);
  { Setup combo box and load document }
begin
  // Store screen fonts in combo box and disabled it
  ComboBox1.Items.Assign(Screen.Fonts);
  ComboBox1.Enabled := False;
  // Load the HTML page
  WebBrowser1.Navigate(ExtractFilePath(ParamStr(0)) + 'Test.html');
end;

Listing 3

Note that we disabled the combo box to prevent a font from being selected. We do this because to set a font we need to access the browser's Document object – and this object is only available when the HTML document is fully loaded. TWebBrowser triggers an OnDocumentComplete event when the document has finished loading. Therefore we handle that event and enable the combo box:

procedure TForm1.WebBrowser1DocumentComplete(Sender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
  { Document loaded: enable combo box }
begin
  ComboBox1.Enabled := True;
end;

Listing 4

We now come to the code that actually calls the JavaScript function. When the user selects a new font the combo box triggers its OnChange event. We handle this event by calling the JavaScript SetFont() function with the name of the selected font, as follows:

procedure TForm1.ComboBox1Change(Sender: TObject);
  { Make browser use selected font }
var
  Doc: IHTMLDocument2;      // current HTML document
  HTMLWindow: IHTMLWindow2; // parent window of current HTML document
  JSFn: string;             // stores JavaScipt function call
begin 
  // Get reference to current document
  Doc := WebBrowser1.Document as IHTMLDocument2;
  if not Assigned(Doc) then
    Exit;
  // Get parent window of current document
  HTMLWindow := Doc.parentWindow;
  if not Assigned(HTMLWindow) then
    Exit;
  // Run JavaScript
  try
    JSFn := 'SetFont(''' + ComboBox1.Text + ''')';
    HTMLWindow.execScript(JSFn, 'JavaScript');
  except
    // handle exception in case JavaScript fails to run
    ShowMessage('Error running JavaScript');
  end;
end;

Listing 5

This method is very similar to that described in the previous section. SetFont() is called with the name of the font selected in the combo box. If an exception is raised when the JavaScript is called we display a message.

We have now developed all the code. All that remains is to add the following uses clause to the implementation section of the unit to enable the program to compile:

uses
  SysUtils, MSHTML, Dialogs;

Listing 6

Demo code

The source code on the code study is available for download. The code was developed using Delphi 7 Professional.

This article is copyright © Peter D Johnson 2002-2007

Licensed under a Creative Commons LicenseOffsite icon.

 

How to call JavaScript functions in a TWebBrowser from Delphi