Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Calling JavaScript functions in the Web Browser Control


:P
On this page:

I’ve been working on a sample application that needs to load some generated display content into the Web Browser control and I’ve been wanting to interact with this content. One of the things I want to do is use some transitions like modal overlays and activity displays while pages load – in other words do some Ajax based UI functionality but with data that is provided by the host application.

The process for this goes something like this:

  • Bring up the form
  • Based on parameters provided as input render the form with HTML content
  • Interact with the page (like reload the display as changes are made or stuff data into the dom)

One of the operations that need to occur are re-freshes as new data is loaded so I like to display modal overlay. So in my page I have some script code that uses jQuery and my internal script library with something simple like this:

<div id="modalDialog" style="width: 100px; height: 100px;">
    <img id="loadingImg" src="loading.gif" />
</div>
<
script type="text/javascript"> function showOverlay(hide) { if (!hide) $("#modalDialog").modalDialog(); else $("#modalDialog").modalDialog("hide"); return "Rick" && just to demonstrate ret parameter } </script>

So, is it possible to call this script function in the page? Absolutely. As you probably know global functions are actually defined on the DOM’s window object so you can access scripts through the document.parentWindow  property of the DOM.

Now if you’re using Winforms .NET and the Web Browser control there’s a WebBrowser.Document property, but it doesn’t have a parentWindow property. The Document object .NET exposes is only a wrapper around the ‘real’ DOM COM object that Internet Explorer uses internally. The COM object is what we need to access in order to get at script code.

In order to do this we need to get the COM document object and then use Reflection to walk the hierarchy to do what essentially amounts to:

oWebBrowser.Document.DomDocument.parentWindow.showOverlay(false);

Here’s what this looks like inside of a small test form that contains only the Web Browser control and a button:

private void Form1_Load(object sender, EventArgs e)
{
    this.Browser.Navigate(@"C:\temp\html\_preview.htm");
}
private void btnOverlay_Click(object sender, EventArgs e)
{
    // This code is much easier than what I came up with – thanks to Jarle in comments
    // who pointed out the uhm obvious  
    object result = this.Browser.InvokeScript("showOverlay", false);

    MessageBox.Show(result.ToString());

    //// *** Get the COM DOM object (not the .NET Wrapper)
    //object doc = this.Browser.Document.DomDocument;

    //// *** Now you can use Reflection on the COM DOM
    //object win = wwUtils.GetPropertyCom(doc, "parentWindow");

    //// *** Call the JavaScript function and capture the result value
    //object result = wwUtils.CallMethodCom(win, "showOverlay", false);

    //MessageBox.Show(result.ToString());
}

The browser is navigated to testpage on startup and the interactive page is loaded. Then when the button is clicked the the overlay is displayed on top of it. The result looks something like this:

 ScriptedDOM

.NET makes this very easy as it has a Browser.Document.InvokeScript() function that can execute any script on the page with parameters and return a result value easily.

Originally I had completely missed this though and instead used COM Interop as you can see in the commented code. Jarle pointed out the obvious in the comments – bit again by the vast functionality in the framework. I was looking for a way to access the Window property of the document, which is where scripts normally are ‘attached’. Alas, Microsoft used Document.InvokeScript, which is in any case much cleaner than having to create a manual wrapper.

Egg on my face. It isn’t the first time I’ve made a simple solution difficult :-}… But I’m keeping this up here for (my own) reference especially in light of some of the complications in the following section.

You can download the .NET sample code from here.

A few gotchas for FoxPro

When I looked into this again I actually was working on a sample FoxPro piece for a conference presentation which has the same requirements. No matter what I tried though I could not get the function call to work calling the document.parentWindow or document.script object. Whenever I would call the method I’d get a method not found error.

After a bunch of experimenting and by accident creating a real simple function that had an all lower case name, I figure out that this is a bug/feature of the way FoxPro does COM interop with COM objects. Specifically it deals with casing and the fact that Visual FoxPro forces all COM functions called to lower case. Since JavaScript is case sensitive, the correct case is required in order for a function to be executed. This means effectively that FoxPro can only call lower case Javascript functions. The other issue is that you HAVE TO pass at least one parameter to the called JavaScript function even if that function does not take a parameter. Again this is an oddity in how VFP translates the COM interfaces to to call this dynamic function and passes it one parameter. This shouldn’t be a problem as you can just ignore the parameter in the JavaScript function.

The following can be done from the command window:

oBrowser = CREATEOBJECT("InternetExplorer.Application") obrowser.Visible = .t. oBrowser.Navigate("C:\wwapps\Conf\FoxWebServices\FoxCode\html\_preview.htm") oBrowser.Document.parentWindow.showoverlay(.F.) ? oBrowser.Document.parentWindow.showoverlay(.F.) ? oBrowser.Document.Script.showOverlay(.T.)

Note that in order for this to work with the script shown above the function name has to be changed to showoverlay() in JavaScript!

Works in the Web Browser Control or InternetExplorer.Application

As you can see in the FoxPro snippet this approach also works with the InternetExplorer.Application browser automation as well as in the Web Browser control. As long as you have access to the DOM document this approach should work regardless how the Web browser is hosted. Note that if you run in Internet Explorer though with scripts from the local machine (ie. not a Web Url) you will get a security warning every time you load the page. You have to allow scripts in the yellow warning bar at the top first before they can be accessed. This makes script access in the IE browser a lot less useful. The Web Browser control doesn’t have this limitation as it assumes you have whatever rights your application already has (or doesn’t have).

Nice

Because of the Fox case issue I had actually thought that this feature didn’t work any longer – suspected some sort of security hack – but it turns out the issue is merely a COM translation issue.  This will come in handy especially in .NET as I need to update some code that does some fairly heavy duty HTML editing code. With the ability to run some of that code in script in the browser and taking advantage of jQuery this will be a lot easier!

Posted in COM  .NET  WinForms  

The Voices of Reason


 

Steve
September 28, 2008

# re: Calling JavaScript functions in the Web Browser Control

How are you using this and why? I'm just curious why you wouldn't use use a web browser.

Rick Strahl
September 28, 2008

# re: Calling JavaScript functions in the Web Browser Control

@Steve - the test data I used (which is amazon Web Service data) is probably a really bad example, but I use the Web Browser control for locally generated content in just about any desktop application I create. And yes I build many of those too in addition to Web apps - I don't believe that everything should run as a Web application - at least not yet <s>.

One prominent example is Help Builder which uses the Web Browser control for preview rendering and HTML Editing. That would be a horrible candidate for a pure Web application.

It's very useful to have access to script code inside of the *locally generated* HTML that gets displayed in the Web Browser control if you're doing complex things like say HTML editing where things need to get injected into the page dynamically.

Jarle
September 28, 2008

# re: Calling JavaScript functions in the Web Browser Control

Hi.

I also needed to execute javascript code within the WebBrowser control and I used the code webBrowser.Document.InvokeScript("JavaScriptFunctionName", new object[1] { parameter1 }), and it worked fine.

Rick Strahl
September 29, 2008

# re: Calling JavaScript functions in the Web Browser Control

@Jarle - Duh - I completely missed this method since I'd been doing this through COM in the first place (in another environment).

Obviously using Document.InvokeScript() is the better solution - same approach.

(egg on my face). Move along - nothing to see here.

Suprotim Agarwal
September 29, 2008

# re: Calling JavaScript functions in the Web Browser Control

Yes. webBrowser.Document.InvokeScript is the way to go. I had posted an article on this a few weeks ago over here:

http://www.dotnetcurry.com/ShowArticle.aspx?ID=194

MikeS
October 01, 2008

# re: Calling JavaScript functions in the Web Browser Control

Hey Rick,

Do you know if it is possible to do this with jquery too? From what I've read on your article about jquery, there are no functions to call do I'm doubting it.

Rick Strahl
October 01, 2008

# re: Calling JavaScript functions in the Web Browser Control

@Mike - not sure what you mean. You can only access global scope functions in the document, but with COM it might be possible to walk all the way into parentWindow.$ to effectively use jQuery from C#. Haven't tried that but it should be possible using COM and Reflection since it's all there in the interfaces.

The question is - why would you want to do this? It'd be much easier to create a function in the document and hten have IT do the work. Even if you have to add the script into the DOM yourself for that matter.

Chandan
October 08, 2008

# re: Calling JavaScript functions via Invokescript

Hi,
I have this javascript code.

function submit_form(f)
{
/* Added on 20010313 by Felix Yu */
/*if (f.type.selectedIndex==0)
{
alert('¤½¶}©Û¼Ð¤½§i¬d¸ß¼È®ÉÃö³¬,½Ð¨Ï¥Î[¨Ì¼ÐªºÄݩʬd¸ß]©Î[¨Ì³æ¦ì¦WºÙ¬d¸ß],ÁÂÁÂ!');
return false;
}
*********************************/
if(uploadcheck())
{
if (flag_chk==false)
{
flag_chk=true;
//alert("¤w¶Ç°e");
f.submit();
return true;
}
else
{
//alert("¸ê®Æ³B²z¤¤¡A½Ðµy«á");
return false;
}
}
else
return false;

}

<input type="button" value="Search" onClick="submit_form(this.form)">



I have to call the invokescript function by passing a parameter this.form....what shall be the statement for invokescript along with the form parameter?...Can anyone please help?

Chandan
October 08, 2008

# re: Calling JavaScript functions in the Web Browser Control

How do I pass a form object via the invokefunction?

Sam
January 03, 2009

# re: Calling JavaScript functions in the Web Browser Control

I would like to know how to get at data generated by a javascript function that doesn't have a return value. For instance when there is javascript that is used to show some data, but it doesn't have a return value.
In this case I'm getting a null value for the object variable (in your provided sample code).
How do I access the data that the javascript is displaying on the page? Is it in a frame? Or somewhere else?
Any help very much appreciated.
Thanks,

-Sam.

TheGuy
February 17, 2009

# re: Calling JavaScript functions in the Web Browser Control

Chandan,

try making a javascript function in your page that does it for you, something like:

function SubmitMyForm(){
if( submit_form( document.theform_or_however_u_reference_it ) )
magical_submit_btn_click_func();

}


Then use Document.InvokeScript("SubmitMyForm"...);

I'm not a javascript/web guy, but that's my guess/suggestion.

Matt Stark
March 01, 2009

# re: Calling JavaScript functions in the Web Browser Control

Hey Rick - Are you aware of a way to call a C# method from JavaScript from within the page running inside the Web Browser control?

Ideally I'd like to respond to a JavaScript click event in C# from the Windows Form.

Rick Strahl
March 01, 2009

# re: Calling JavaScript functions in the Web Browser Control

@Matt - I believe you can call a JavaScript function as described above and pass a reference of a C# object. Attach that to the document as a global variable and you should then be able to call methods on that object from JavaScript code. Haven't tried this, but this used to work for me with FoxPro code in the past.

This assumes the Web Browser control is running in a security environment that allows this however.

Steve
February 18, 2010

# re: Calling JavaScript functions in the Web Browser Control

I'm trying to scrape some data in the wb control after a "Next Page" uses some Ajax to load a fresh batch of data. However it's "not there" (Page 1 data still is).

Do you know of a way to access this? Plus, on a W2K machine the Ajax appears not to kick in and paging is done "normally". Thought of trying to disable js. Or perhaps there is a jQuery function that can read the dynamically loaded data...?

Tim Swanson
March 15, 2010

# re: Calling JavaScript functions in the Web Browser Control

There's a typo in your first C# code snippet.

object result = this.Browser.InvokeScript("showOverlay", false);


should be

object result = this.Browser.Document.InvokeScript("showOverlay", false);


Thanks for this post. It was exactly what I was looking for.

mishi
March 19, 2010

# re: Calling JavaScript functions in the Web Browser Control

Hi Rick,
Thanks for the post. I'm facing an issue with getting the document object from the web browser control. This works if the document I navigate to is on the disk (local or UNC path). However if I have this hosted in IIS and provide the url as http://localhost/mytest.htm I cannot get the document object. I am launching this from an activeX control that is embeded within an aspx page.

I am using C++ (since our legacy activeX is in C++) and the code is something like:
COleVariant* pvarURL = new COleVariant( _plugInDetails.pluginURL.c_str() );
COleVariant* pvarEmpty = new COleVariant;
_browser.Navigate2( pvarURL, pvarEmpty, pvarEmpty, pvarEmpty, pvarEmpty );
delete pvarURL;
delete pvarEmpty;

LPDISPATCH spDisp = _browser.GetDocument();
//this line immediately returns NULL - if this url is of the form http://xyz. If the url is on disk all goes well.


Any ideas? Btw the activeX control is marked safe for scripting and initializing.

Thanks

Gilberto Ruiz
May 20, 2010

# re: Calling JavaScript functions in the Web Browser Control

Hi, Rick.

this post was very usefull to me.

But I need do something before I gonna crazy!

In a web page, I have the follow script :

<A onclick=javascript:objTop.frameQueryBy.fnShowCaseDetails(11,12) onMouseOver="javascript:objTop.frameQueryBy.fnReclassificar('N','N'); Fprot(0 , 'NSNNNSSNSNNS' );>click here</a>

I need that my vfp code run the onMouseOver code.

I try to run the 2 functions on it but not work.

tks

Gilberto - Brazil

Yasir Godil
August 30, 2010

# re: Calling JavaScript functions in the Web Browser Control

Is this possible to load a page in Web browser control and then let user right click every control and perform some action available on menu. I must be able to know which button on menu was clicked and what was the ID or Name of the html control on which user right clicked ?

Expert Comment
September 10, 2010

# re: Calling JavaScript functions in the Web Browser Control

You can ClientScriptManager clss method RegisterClientScriptBlock or RegisterStartupScript method to invoke JavaScript Method.

Refer to below links for more details about the same.

http://www.a2zmenu.com/CSharp/Calling%20JavaScript%20function%20from%20CSharp.aspx

Abupama
January 06, 2011

# re: Calling JavaScript functions in the Web Browser Control

yes nice post.
You can also call C# object methods from javascript in Microsoft webbrowser control.
by using window.external object...
http://bimbim.in/post/2011/01/06/C-Sharp-Calling-Dotnet-function-from-JavaScript-and-Accessing-HTML-element-from-Dotnet-with-Web-browser-control.aspx

Peter E.
December 09, 2014

# re: Calling JavaScript functions in the Web Browser Control

Hi,
I read this thread with interest, but I have one big concern.
All this will only work with the Visual Studio Browser Control, therefore the website will be embedded within the forms application.

But my problem is, that I have to find a solution to control a web application provided from an other company. And they won't like to embed their application within a forms app.

I'm seeking for a solution to control a standalone instance of IE 11. I found that with "SHDocVw.dll" and "msthml" I might do this job with this piece of code:

SHDocVw.InternetExplorerMedium IE = null;
IWebBrowserApp wb = null;
 
...
IE = new SHDocVw.InternetExplorerMedium();
IE.Visible = true;
 
...
string URL = txtURL.Text; // text input field with the URL to navigate
wb = (IWebBrowserApp)IE;
wb.Navigate(URL, ref Empty, ref Empty, ref Empty, ref Empty);
 
..
 
mshtml.HTMLDocument _document = (mshtml.HTMLDocument)IE.Document;
object result = _document.parentWindow.execScript("showOverlay();", "javascript");


But I don't get the result from the JavaScript back!
After searching in the web, I found in a Delphi forum that "parentWindow.execScript" doesn't return result codes in any case!

Has somebody a solution for this?

BR
Peter

Michel Laplane
November 08, 2017

# re: Calling JavaScript functions in the Web Browser Control

If the Javascript function is not directly in the HTML file the function is not reachable.

===========in library.js

function MyFunctionUnknown()
{
  alert("hello unknown");
}

=========in C# browserControl file

webBrowser.Document.InvokeScript("MyFunction"); // Works well

webBrowser.Document.InvokeScript("MyFunctionUnknown"); // don't works

Any suggestions?

Best regards.


Rick Strahl
November 08, 2017

# re: Calling JavaScript functions in the Web Browser Control

@Michel - Yup that's because externally loaded script is not directly tied to the document. You can use document.parentWindow.MyFunctionUnknown() instead to call that. Make sure the function you call is global (ie. part of window) and that the document and script are properly and completely loaded.


Mark Harpenau
May 13, 2018

# re: Calling JavaScript functions in the Web Browser Control

Yay Thank you Rick! I just found this blog, and it helped me solve an issue that i've tried to solve unsuccessfully for years with my custom VFP Html editor. ...Because you mentioned the golden tidbit about having to supply a dummy parameter to the webbrowser window node functions, even though a function may not require one. This solved the reason that document.parentWindow.getSelection() (also available as document.getSelection() ) wasn't working in ie11. i've had to keep the webbrowser at ie10 (via the browserEmulation registry setting) because getSelection() wouldn't work until i read your blog and tried it with a dummy parameter. Your blogs are so useful! Even after 10 years!

In case this helps anyone, here are 2 clips of VFP code...

This code is how i successfully get the current selection range (and parent node) of where the current cursor is:

* m.toWebEdit.xoHostIe.xoie is a ref to instance of webbrowser control

m.lo1= m.toWebEdit.xoHostIe.xoie.document

IF TYPE("m.lo1.getselection") = "O"			&& if ie11
	m.lo1= m.lo1.getselection(.f.).getRangeAt(0)    && pass dummy param .f.
	m.lo2= m.lo1.startcontainer     && node containing current selection
ELSE
	m.lo1= m.lo1.selection.createRange()
		&& createrange() does not work in ie11. we get obj with no props

	IF TYPE("m.lo1.item") = "O"
		m.lo2= m.lo1.item				&& ex. will be img if img selected
	ELSE 
		m.lo2= m.lo1.parentElement		&& elem containing current selection
	ENDIF 
ENDIF

And this code below allows inserting text at the current selection. Note: pasteHtml() is not avail in ie11:

* m.to1 is a ref to current selection.
* m.tcText is text to insert

IF TYPE("m.to1.createContextualFragment") = "O"			&& if ie11
	m.to1.insertNode(m.to1.createContextualFragment(m.tcText))
ELSE
	IF TYPE("m.to1.text") <> "C"
		dspmsg("!O1", "The cursor is not within a Text area")
		RETURN .f.
	ENDIF 
	m.to1.pasteHTML(m.tcText)
ENDIF


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024