How to call JavaScript functions in a TWebBrowser from Delphi
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 License.
How to call JavaScript functions in a TWebBrowser from Delphi
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
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;