How to get actual JavaScript value in onclick from webbrowser control?
Asked Answered
O

6

14

I'm looking for a way to get the JavaScript code defined inside of onclick. I'm using .NET 2.0 C# Visual Studio 2005.

Example:

<span id="foo" onclick+"window.location.href='someURL'>click here</span>

My goal is to get the string "window.location.href='someURL'".

Scenario:

A user clicks on web page element, the tag shown above for instance, inside of WebBrowser control. Then the clicked tag is refereed to HtmlElement object.

In WebBrowser control I then call HtmlElement object's getAttribute("onclick"), it just gives me "System.__ComObject".

I've searched how to deal with it then found that it can be casted then get the value.

if (tag.GetAttribute("onclick").Equals("System.__ComObject"))
{
    Console.WriteLine("dom elem  >>>>>>>>>>> " + tag.DomElement.ToString());
    mshtml.HTMLSpanElementClass span = (mshtml.HTMLSpanElementClass)tag.DomElement;

    Console.WriteLine("js value ===>" + span.onclick);
}

Output:

dom elem  >>>>>>>>>>> mshtml.HTMLSpanElementClass
js value ===> System.__ComObject

As it shown, span.onclick still give me System.__ComObject, what am I doing wrong?

In Why does HtmlElement's GetAttribute() method return “mshtml.HTMLInputElementClass” instead of the attribute's value? this guy said it worked in his case, and I've followed it, but mine is somewhat not working...

UPDATE

Research, research.....

I can add reference VisualBasic.dll to my C# project then call the method to find out who is this System.__ComObject really is.

Console.WriteLine(Microsoft.VisualBasic.Information.TypeName(span.onclick));

Output:

JScriptTypeInfo

It looks like this is a JScript type... how can I access this object?

More detail

The above description is based on my current project. The project is to create something like Selenium IDE. It uses WebBrowser control instead.

Selenium IDE creates 3 different things to record an element in the web document.

1. actionType
2. xpath
3. value

For instance,

type, //input[@id=foo], "hello world"
clickAndWait, //link=login, ""

Selenium IDE recognize page load so it changes actionType between "click" and "clickAndWait". My case, I want to make it simple.

If I click on the element and if it is anchor tag or has page load kind of javascript such as onclick=window.location.href='blah' then I want to set the actionType to "clickAndWait".

Oliveira answered 3/6, 2011 at 6:42 Comment(9)
looks like span.onclick is actually IDipatchPreschool
Maybe I'm totally going the wrong way but can't you just add runat="server" and access the onclick attribute?Limpopo
@RBaarda: I didn't know what runat="server" is, it looks like attribute I can add to the html tag but in my case, I'm not the web page author. I'm just using WebBrowser control for some random site.Oliveira
Are you developing a Windows application with web browser control? You want to achieve the same functionality that the IE Developer Inspect element has?Foetation
@Amit: that would be awesome though could be overkill but the more the better :)Oliveira
@masato-san how did you solve this problem at the end?Cacuminal
@dr.evil: I have not solved yet.. I'm currently away from this part of project but will be back sometime when I get time.Oliveira
@masato-san Did you get a chance to resolve it? It would be great if you can post the answer.Efficacious
See System.__ComObject is returned when I use getAttribute. Use attributes("onclick").value.ToString().Nealson
N
9

There are number of ways you can do it.

  1. There is an Event object in DOM, which will give you information about which element generated this event.
  2. You can look at here, http://msdn.microsoft.com/en-us/library/ff975965%28v=VS.85%29.aspx
  3. This one is good, you can use this easily, you will get the event object as method parameter which you can investigate parameters to find out the source of the event. http://support.microsoft.com/kb/312777

Another alternative is to use a custom navigation url and act upon it

  1. Override BeforeNavigate event
  2. Check for Navigation url if it contains "mycommand:click" or "mycommand:clickandwait" 3. If it contains any of this, then set cancel as true. (this will stop navigation by browser).
  3. Then you can navigate your webbrowser code from your C# code and pass cancel as true.

Another Alternative method is to use External object, WebBrowser allows you to set an ObjectForScripting which you can access within Javascript of HTML.

ObjectForScripting in .NET 2.0

[ComVisible(true)]
public class MyClass
{
   // can be called from JavaScript
   public void ShowMessageBox(string msg){
       MessageBox.Show(msg);
   }
}

myBrowser.ObjectForScripting = new MyClass(); 
// or you can reuse instance of MyClass

And you can call,

window.external.ShowMessageBox("This was called from JavaScript");
Nitroso answered 9/6, 2011 at 13:23 Comment(11)
Thanks for the list, for #2 it only became available on IE9 but my environment is .NET framework 2.0 with IE6 so I don't think I can relay on #2.Oliveira
And for #3, I'm not using SHDocVw.dll I'm just using mshtml.dll. Like my event handler only is declared: private void ClickEventHandler(object sender, HtmlElementEventArgs e) so #3 I think is not also the option...Oliveira
But why don't you try the alternative way of using your own URL scheme and cancel navigation in which you will get the URL that was passes.Nitroso
@Akash Kava: yep I think I will try that way. If it works, would be pretty slick! :)Oliveira
I think I can use "Navigating" event handler since I don't have "BeforeNavigate" available..msdn.microsoft.com/en-us/library/…Oliveira
@Akash: by the way the navigation url does not contain "mycommand:click", it just gives me the url it is pointing (i.e. www.msn.com/news etc).Oliveira
Yes, but you can make your own urlscheme, like you can put mycommand:Command1 , mycommand:Command2 and based on Command1 and Command2 you can control your logic, this is how every software does it. Navigating and BeforeNavigate are same events, I think one was in WinForm and another is in WPF. Have you tried External Interface access? If you set webBrowser.ExteranlObject (I dont know exact name) = myClass, myClass should be Com Visible and you can call its methods directly from javascript too.Nitroso
@Akash: So, do you mean I can get webbrowser.URL value and concatenate "mycommand:command1" ??? I'm confused how to use it. The thing is a user clicks a button for instance but there is no way for me to tell if it requires wait or not (i.e. the button may invoke js to navigate to different page. ) (btw ObjectForScripting is supported from .NET 3.5, my environment is 2.0 thanks for the reference though..)Oliveira
ObjectForScripting is also supported in .NET 2.0, please check WebBrowser control's MSDN in WinForms, it is there. In navigating event, you can check e.Uri.ToString() and you can see if it is your style like mycommand:command1, if it contains mycommand: then you can cancel the event or else you can just return, webbrowser will navigate to any other page just normally.Nitroso
I have added link for ObjectForScripting in .Net 2.0Nitroso
I voted up your answer because it helped, also really appreciate the answers you (and all other ppl) provided, but still I have not gotten to the point where I can make it happen.. (also I was away for a while for other projects..) when I have time to come back here and confirm that it worked, then I should mark the answers. (sorry for taking very long time)Oliveira
S
4

Cast the element object to mshtml.IHTMLDOMNode, then read the attributes via IHTMLDOMNode.attributes. HtmlElement.GetAttribute is getting the IDispatch interface of the jscript function generated from the embedded attribute.

Sextuplicate answered 14/6, 2011 at 6:11 Comment(5)
Thanks, but I'm using System.Windows.Form.HtmlElement and casting its instance to mshtml.IHTMLDOMNode is compile error.Oliveira
I could do mshtml.IHTMLDOMNode castedElement = (mshtml.IHTMLDOMNode) elem.DomDocument; Console.WriteLine(castedElement.attributes); to cast but still gives me System.COM_Object.Oliveira
IHTMLDOMNode.attributes returns the attribute collection's IDispatch interface (note the s at the end of the property name). The linked sample enumerates the collection via the IHTMLAttributeCollection interface and check the attributed name via the IHTMLDOMAttribute interface of each item in the collection.Phung
Is there any way to do this without enumerating all attributes (which is really slow) but directly using something getAttribute ?Cacuminal
I isolated this bug in IE - this fails <form name="something" action="/test" method="post" ><input type="hidden" name="action"/></form> but this won't <form name="something" action="/test" method="post" ><input type="hidden" name="actionx"/></form> So you can't have a form item with name of the attribute that you are trying to read. This fails for GetAttribute("action") and this will fail for GetAttribute("method") >> <form name="something" action="/test" method="post" ><input type="hidden" name="method"/></form> . Is this bug well known by IE team, or where to report MS Connect?Cacuminal
E
3

As per Sheng Jiang's response, here is some working sample:

IHTMLElement element = YourCodeToGetElement();
string onclick = string.Empty;

IHTMLDOMNode domNode = element as IHTMLDOMNode;
IHTMLAttributeCollection attrs = domNode.attributes;

foreach (IHTMLDOMAttribute attr in attrs)
{
    if (attr.nodeName.Equals("onclick"))
    {
        string attrValue = attr.nodeValue as string;
        if (!string.IsNullOrEmpty(attrValue))
        {
            onclick = attr.nodeValue;
            break;
        }
    }
}
Edema answered 10/11, 2011 at 3:24 Comment(0)
P
2

You can try to parse webBrowser1.DocumentText property using HtmlAgilityPack and then get desired result using XPath.

Preschool answered 6/6, 2011 at 12:37 Comment(1)
Thanks for the info. The problem is though I need to click on the element with onclick (ex <div onclick='foo();'>) in the webbrowser and then need to find what's in the onclick (i.e. foo(); ). In my scenario I cannot know the xpath beforehand.Oliveira
T
2

If you don't HAVE to do it with C# (you can do it with JS and create a Postback) you should take a look at THIS question.

Travel answered 9/6, 2011 at 14:36 Comment(0)
H
2

You can parse it yourself easily, by first reading obj.outerHtml. That should give you the entire html for that obj, then search it for the value onclick="????" and extract the ???? part.

Hexapartite answered 9/4, 2012 at 23:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.