Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2020年05月25日] [更新 2020年05月25日] Delphi 一般・その他
TWebBrowser における JavaScript の実行と戻り値の取得
動作確認等 Windows 7 U64(SP1) + Delphi XE5(UP2) Pro VCL-32
[整理番号 - 1090]







JavaScript の関数の実行と戻り値の取得


Delphi の TWebBrowser コンポーネントで JavaScript を実行し,戻り値や JavaScript 内の変数の値を取得するサンプルです.
参考リンクの 4 番目の記事にも JavaScript を実行するサンプルがあります.window.external のサンプルは、JavaScript 内から Delphi のメソッドを実行するサンプルです.



本ページのサンプルコードでは,インターフェイスの確認,あるいはオブジェクトのチェックを省略しています.以下のようにしておけば,エラーの発生が抑止でき,例外でプログラムが停止することを抑止できます.


   if not Supports(WebBrowser1.Document, IHTMLDocument3, LIDocument3) then Exit;

    LIElement :=  LIDocument3.getElementById('result1');
    if LIElement = nil then Exit;


QueryInterface 関数を使用する場合は,以下のように処理できます.


   if WebBrowser1.Document.QueryInterface(IHTMLDocument3, LIDocument3) <> S_OK then Exit;




01_JavaScript の引数なしの関数の実行


IHTMLWindow2 の execScript メソッドを使用すると,TWebBrowser コンポーネントで HTML コード内に書かれている JavaScript の関数を実行することができます.その例です.
実行する JavaScript の関数名は文字列として渡します.このサンプルは引数なしの関数を実行する例です.引数がない場合は,関数名の末尾に () が必要です.

このサンプルでは,フォーム生成時に HTML コードを読み込んで TWebBrowser で表示しています.



図1
JavaScript の関数の実行結果
  • このデイアログは JavaScript 内の関数が表示している
  • TWebBrowser のテキストも JavaScript の関数内のコードで表示している

まず,以下のフォルダにライブラリのパスを通しておきます.



リスト1
テストに使用した HTML コート.ファイル名は JaveScriptTest.html
<!DOCTYPE html>
<html>
<head>
<script>
  var count = 0;

  function testFunction() {
    ret = confirm("確認したら OK をクリック");
    if (ret == true){
      count = count + 1;
      resstr = "確認済み " + count + " 回目";
      document.body.insertAdjacentHTML("beforeend", resstr);
    }  
  }  
</script>
</head>

<body>
</body>
</html>


リスト2
テスト用のフォームのユニットのコード
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.OleCtrls, SHDocVw,
  Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    WebBrowser1: TWebBrowser;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  MSHTML;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//  テスト用の HTML コードを読み込んで表示
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LURL : string;
begin
  LURL := ExtractFilePath(Application.ExeName) + 'JaveScriptTest.html';
  WebBrowser1.Navigate(LURL);
end;

//=============================================================================
//  JavaScript の関数の実行
//  引数なしの場合
//
//  uses に MSHTML が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LIDocument2 : IHTMLDocument2;
  LIWindow2   : IHTMLWindow2;
  LJScriptStr : string;
begin
  LIDocument2 := (WebBrowser1.Document as IHTMLDocument2);
  LIWindow2   := LIDocument2.parentWindow;
  try
    LJScriptStr := 'testFunction()';
    LIWindow2.execScript(LJScriptStr, 'JavaScript');
  except
  end;
end;

end.




02_JavaScript の文字列型引数付き関数の実行


JavaScript の関数の引数に文字列を渡す場合は,文字列をダブルクォーテーションで括ります.
ダブルクォーテーションを含む文字列を文字列として引数に渡します.



図2
JavaScript の関数の実行結果ー
  • このデイアログは JavaScript 内の関数が表示している
  • 表示している値は JavaScript の関数の引数に渡した文字列

リスト3
テストに使用した HTML コート.ファイル名は JaveScriptTest.html
<!DOCTYPE html>
<html>
<head>
<script>
  function testFunction(arg) {
    alert (arg);
  }  
</script>
</head>

<body>
</body>
</html>


リスト4
テスト用のフォームのユニットのコード
フォーム OnCreatae イベントのコードは最初のサンプルと同じ
//=============================================================================
//  JavaScript の関数の実行
//  引数が文字列の場合
// 引数の文字列をシングルまたはダブルクォーテーションで括る
//
//  uses に MSHTML が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LIDocument2 : IHTMLDocument2;
  LIWindow2   : IHTMLWindow2;
  LArgStr     : string;
  LJScriptStr : string;
begin
  LIDocument2 := (WebBrowser1.Document as IHTMLDocument2);
  LIWindow2   := LIDocument2.parentWindow;
  try
    // 以下 (シングルクォーテーション 3 つ) でもよい
    // LArgStr := '''引数の文字列!''';
    LArgStr := '"引数の文字列!"';
    LJScriptStr := 'testFunction(' + LArgStr + ')';
    LIWindow2.execScript(LJScriptStr, 'JavaScript');
  except
  end;
end;


文字列が変数の場合は, 変数名をダブルクォーテーションで括ります.


リスト5
    // 文字列が変数の場合
    // 文字列にダブルクォーテーションを追加する
    // LArgStr := '"' + LArgStr + '"';
    LArgStr := '引数の文字列!';
    LArgStr := AnsiQuotedStr(LArgStr, '"');




03_JavaScript の数値型引数付き関数の実行


JavaScript の関数の引数に数値を渡す場合は,数値を文字列にして引数に渡します.



図3
JavaScript の関数の実行結果
  • このデイアログは JavaScript 内の関数が表示している
  • 表示値は JavaScript の関数の引数に渡した数値と関数内の値の加算値

リスト6
テストに使用した HTML コート.ファイル名は JaveScriptTest.html
<!DOCTYPE html>
<html>
<head>
<script>
  function testFunction(arg) {
    intval = 50000000 + arg;
    alert (intval);
  }  
</script>
</head>

<body>
</body>
</html>


リスト7
テスト用のフォームのユニットのコード
フォーム OnCreatae イベントのコードは最初のサンプルと同じ
//=============================================================================
//  JavaScript の関数の実行
//  引数が数値の場合は文字列にして渡す
//
//  uses に MSHTML が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LIDocument2 : IHTMLDocument2;
  LIWindow2   : IHTMLWindow2;
  LArgInt     : Integer;
  LJScriptStr : string;
begin
  LIDocument2 := (WebBrowser1.Document as IHTMLDocument2);
  LIWindow2   := LIDocument2.parentWindow;
  try
    LArgInt := 123450;
    LJScriptStr := 'testFunction(' + IntToStr(LArgInt) + ')';
    LIWindow2.execScript(LJScriptStr, 'JavaScript');
  except
  end;
end;




04_JavaScript の実行結果 ( JavaScript 内の変数の値) を取得


JavaScript 内の変数の値を Delphi 側のコードで取得する例です.JajaScript の関数の実行結果を特定の変数に代入しておけば,その変数の値を取得することができます.
TWebBrowser に表示する HTML コードに以下の要素 (コード) を追加しておきます.この例では id の値は result1 となっています.この id の値 (文字列) は,対象の HTML コード内にはない一意の値とします.


  <input type='hidden' id='result1' value=''>

JavaScript 内の関数内で,この要素の value 属性の値に,Delphi 側で取得したい値を代入して置き換えます.この置き換えた値を DOM 操作で取得します.
このサンプルの方法では,JavaScript の関数の return 値は取得できません.



図4
JavaScript の関数の実行結果
  • JavaScript 内の変数の値を取得して表示

リスト8
テストに使用した HTML コート.ファイル名は JaveScriptTest.html
<!DOCTYPE html>
<html>
<head>
<script>
  function testFunction() {
    retval = 50000000;
    document.getElementById('result1').value=retval;
  }  
</script>
</head>

<body>
  <input type='hidden' id='result1' value=''>
</body>
</html>


リスト9
テスト用のフォームのユニットのコード
フォーム OnCreatae イベントのコードは最初のサンプルと同じ
//=============================================================================
//  JavaScript の関数の実行
//  関数の戻り値を取得( JavaScript 内の変数の値の取得 )
//
//  uses に MSHTML が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LIDocument2 : IHTMLDocument2;
  LIDocument3 : IHTMLDocument3;
  LIWindow2   : IHTMLWindow2;
  LIElement   : IHTMLElement;
  LJScriptStr : string;
  LValueText  : string;
begin
  LIDocument2 := (WebBrowser1.Document as IHTMLDocument2);
  LIWindow2   := LIDocument2.parentWindow;
  try
    LJScriptStr := 'testFunction()';
    LIWindow2.execScript(LJScriptStr, 'JavaScript');


    // JavaScrip の関数の実行結果を取得
    // この例では id の値が result1 の value 属性の値を取得  
    LIDocument3 := WebBrowser1.Document as IHTMLDocument3;
    LIElement  :=  LIDocument3.getElementById('result1');
    LValueText  := LIElement.getAttribute('value', 0);

    MessageBox(Handle, PChar(LValueText), '結果', MB_ICONINFORMATION);
  except
  end;
end;




05_JavaScript の関数の return 値の取得


JavaScript の関数の return 値を取得する例です.
方法としては前項と同じですが,JavaScript の eval 関数を使用して,以下の要素の value 属性に関数の実行結果を代入します.この置き換えた値を Delphi 側から DOM 操作で取得します.
本サンプルては,以下のコードを実行時に HTML の要素として挿入しています.Button2 クリックで挿入するようにしていますので,Button2 をクリックしてから Button1 をクリックしないとスクリプトのエラーが発生します.


  <input type='hidden' id='result1' value=''>


図5
JavaScript の関数の実行結果
  • JavaScript の関数の return 値を取得して表示
  • Button2 をクリックしてから Button1 をクリックする

リスト10
テストに使用した HTML コート.ファイル名は JaveScriptTest.html
<!DOCTYPE html>
<html>
<head>
<script>
  function testFunction() {
    return "retrun value";
  }  
</script>
</head>

<body>
</body>
</html>


リスト11
テスト用のフォームのユニットのコード
フォーム OnCreatae イベントのコードは最初のサンプルと同じ
//=============================================================================
//  JavaScript の関数の実行
//  関数の戻り値を取得
//
//  uses に MSHTML が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LIDocument2 : IHTMLDocument2;
  LIDocument3 : IHTMLDocument3;
  LIWindow2   : IHTMLWindow2;
  LIElement   : IHTMLElement;
  LIdValue    : string;
  LJScriptStr : string;
  LValueText  : string;
  LJSText     : string;
begin
  LIdValue := 'result1';

  LIDocument2 := (WebBrowser1.Document as IHTMLDocument2);
  LIWindow2   := LIDocument2.parentWindow;
  try
    LJScriptStr := 'testFunction()';

    // 関数の実行結果を指定要素の value 属性の値にセットするためのコード
    LJSText := 'eval("tagelement = document.getElementById(''' + LIdValue + ''');'
                   + 'tagelement.value =' + LJScriptStr + '");';

    LIWindow2.execScript(LJSText, 'JavaScript');


    // JavaScrip の関数の実行結果を取得
    // この例では id の値が result1 の value 属性の値を取得  
    LIDocument3 := WebBrowser1.Document as IHTMLDocument3;
    LIElement  :=  LIDocument3.getElementById(LIdValue);
    LValueText  := LIElement.getAttribute('value', 0);

    MessageBox(Handle, PChar(LValueText), '結果', MB_ICONINFORMATION);
  except
  end;
end;

//=============================================================================
//  実行時に HTML のノードを追加
//  input タグの例
//
//  uses に MSHTML が必要
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LIDocument2 : IHTMLDocument2;
  LIDocument3 : IHTMLDocument3;
  LIElement   : IHTMLElement;
  LIdValue    : string;
  LBodyNode   : IHTMLDOMNode;
  LNewNode    : IHTMLDOMNode;
begin
  LIdValue := 'result1';

 if not Supports(WebBrowser1.Document, IHTMLDocument2, LIDocument2) then Exit;
 if not Supports(WebBrowser1.Document, IHTMLDocument3, LIDocument3) then Exit;

  // コードが追加済みなら作成しない
  if LIDocument3.getElementById(LIdValue) <> nil then Exit;

  // input タグの要素を作成して属性を設定
  LIElement :=  LIDocument2.createElement('input');
  LIElement.setAttribute('type', 'hidden', 0);
  LIElement.id := LIdValue;
  LIElement.setAttribute('value', '', 0);

  if not Supports(LIDocument2.body, IHTMLDOMNode, LBodyNode) then Exit;
  if not Supports(LIElement, IHTMLDOMNode, LNewNode) then Exit;

  // HTML に コードを追加
  LBodyNode.appendChild(LNewNode);
end;




06_JavaScript のコードを実行時に HTML 要素として追加


実行時に,HTML の要素として JavaScript の関数のコードを挿入する例です.
このサンプルでは,関数のコードを書いたファイルを読み出していますが,コード中で作成することも考えられます.



図6
JavaScript の関数の実行結果
  • このデイアログは JavaScript 内の関数が表示している

リスト12
テストに使用した HTML コート.ファイル名は JaveScriptTest.html
<!DOCTYPE html>
<html>
<head>
</head>

<body>
</body>
</html>


リスト13
実行時に HTML に追加したコード.javaScript の関数のコード
function testFunction() {
  alert ("実行確認ダイアログ");
}


リスト14
テスト用のフォームのユニットのコード
フォーム OnCreatae イベントのコードは最初のサンプルと同じ
//=============================================================================
//  JavaScript の関数の実行
//  JavaScirp のコードを実行時に HTML に追加
//
//  uses に MSHTML が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LStrList    : TStringList;
  LIDocument2 : IHTMLDocument2;
  LIWindow2   : IHTMLWindow2;
  LIDOMNode   : IHTMLDOMNode;
begin
  LIDocument2 := (WebBrowser1.Document as IHTMLDocument2);
  LIWindow2   := LIDocument2.parentWindow;
  LStrList    := TStringList.Create;
  try
    LStrList.LoadFromFile('JavaTest_JS.txt');

    // JavaScript のノードを追加して,そこに関数のコードを追加
    LIDOMNode := LIDocument2.createElement('script') as IHTMLDOMNode;
    (LIDOMNode as IHTMLScriptElement).text := LStrList.Text;
    (LIDocument2.body as IHTMLDOMNode).appendChild(LIDOMNode);


    // JavaScript の関数を実行
    LIWindow2.execScript('testFunction()', 'JavaScript');
  finally
    FreeAndNil(LStrList);
  end;
end;