Adding a script element to an existing TWebBrowser document

Prompted by a couple of SO qs this weekend, I decided to see if I could work out how to add some javascript in an Html element to a document loaded in a TWebBrowser. Doing so, I've run into an oddity which maybe someone can explain.

Here's my Html document

<html>
  <body>
  Something
  <br>
  <div id="forscript">some more text</div>
  </body>
</html>

and my javascript element I want to add to it

<script type="text/javascript" defer="false">{alert('hello');}</script>

which I pass as the Script argument to AddScript() below.

This is my code (doc2 is an IHtmlDocument2 obtained from the WebBrowser):

procedure TForm1.AddScript(const Script : String);
var
  Element : IHtmlElement;
  Doc3 : IHtmlDocument3;
begin
  Doc2.QueryInterface(IHtmlDocument3, Doc3);
  Element := Doc3.GetElementByID('forscript');
  Element.innerHTML := Element.innerHTML + Script;
end;

This works fine, but ...

The reason for the Element.innerHTML on the RHS of the assignment is simply that I found that if the RHS contains only the Script js, the js does't execute. My question is, why not, and is there a simpler alternative, like somehow creating an IHtmlScriptElement in code and inserting it in the DOM? Obviously my simple-minded work-around is simply to prepend the text the Element already contains, but I have a slightly hard time believing that people who actually know what they're doing would find that necessary.

FWIW #1: I tried using Element.insertAdjacentHtml to add the script but got the same behaviour as I've just described, in terms of needing to insert something in addition to the script to get the script to be executed, so I'm wondering whether it's something to do with how the DOM is processed after a change is made to it.

FWIW #2: I've used the Element.innerHtml route because TWebBrowser/MSHTML.Pas have resisted my attempts to create an IHtmlScriptElement in code; AFAICS, attempting to use any of the CohtmlXXXElement routines in MSHTML.Pas provoke a "Class not registered" exception, which seems to confirm a comment I came across somewhere by @kobik of this parish.

(Btw, using D7 + IE11 on Win7 64-bit)

Answers:

Answer

Here' a complete example how to use IHtmlScriptElement. Html is loaded at application startup. The code under Button1Click adds the javascript to the DOM and executes it:

unit u_frm_SO27091639;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, OleCtrls, SHDocVw, MsHtml, ActiveX;

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure LoadDocFromString(Webbrowser : TWebbrowser);

var
  Strm    : TStringStream;
  Adapter : TStreamAdapter;

begin
 WebBrowser.Navigate('about:blank');
 // warning - don't use this trick in production code, use the `OnDocumentComplete` event
 while WebBrowser.ReadyState <> READYSTATE_INTERACTIVE do
  Application.ProcessMessages;
 // end of warning
 Strm := TStringStream.Create;
 try
  Strm.WriteString('<html><body>Something<br></body></html>');
  Strm.Seek(0, 0);
  Adapter := TStreamAdapter.Create(Strm);
  (WebBrowser.Document as IPersistStreamInit).Load(Adapter) ;
 finally
  Strm.Free;
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);

var
  Doc2     : IHtmlDocument2;
  Script   : IHtmlDOMNode;
  HTMLWindow: IHTMLWindow2;

begin
 Doc2 := Webbrowser1.Document as IHtmlDocument2;
 if Assigned(Doc2.body) then
  begin
   Script := Doc2.createElement('script') as IHTMLDOMNode;
   (Script as IHTMLScriptElement).text := 'function helloWorld() { alert("hello world!") }';
   (Doc2.body as IHtmlDomNode).appendChild(Script);
   HTMLWindow := Doc2.parentWindow;
   if Assigned(HTMLWindow) then
    HTMLWindow.execScript('helloWorld()', 'JavaScript')
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 LoadDocFromString(Webbrowser1);
end;

end.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.