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".

Community
  • 1
  • 1
Meow
  • 18,371
  • 52
  • 136
  • 180
  • 1
    looks like span.onclick is actually IDipatch – Poma Jun 05 '11 at 14:43
  • Maybe I'm totally going the wrong way but can't you just add runat="server" and access the onclick attribute? – RBaarda Jun 06 '11 at 12:05
  • @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. – Meow Jun 07 '11 at 01:28
  • Are you developing a Windows application with web browser control? You want to achieve the same functionality that the IE Developer Inspect element has? – Amit Bagga Jun 08 '11 at 18:30
  • @Amit: that would be awesome though could be overkill but the more the better :) – Meow Jun 10 '11 at 10:05
  • 1
    @masato-san how did you solve this problem at the end? – dr. evil Nov 15 '11 at 14:19
  • @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. – Meow Apr 10 '12 at 00:10
  • @masato-san Did you get a chance to resolve it? It would be great if you can post the answer. – LCJ Mar 31 '14 at 20:14
  • See [System.__ComObject is returned when I use getAttribute](https://stackoverflow.com/questions/9707869/system-comobject-is-returned-when-i-use-getattribute). Use `attributes("onclick").value.ToString()`. – Sam Hobbs Oct 13 '17 at 17:49

6 Answers6

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");
Akash Kava
  • 39,066
  • 20
  • 121
  • 167
  • 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. – Meow Jun 13 '11 at 04:17
  • 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... – Meow Jun 13 '11 at 04:19
  • 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. – Akash Kava Jun 13 '11 at 06:35
  • @Akash Kava: yep I think I will try that way. If it works, would be pretty slick! :) – Meow Jun 13 '11 at 07:21
  • I think I can use "Navigating" event handler since I don't have "BeforeNavigate" available..http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowser.navigating.aspx – Meow Jun 13 '11 at 08:30
  • @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). – Meow Jun 13 '11 at 08:58
  • 1
    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. – Akash Kava Jun 13 '11 at 09:06
  • @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..) – Meow Jun 14 '11 at 07:44
  • 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. – Akash Kava Jun 14 '11 at 09:58
  • I have added link for ObjectForScripting in .Net 2.0 – Akash Kava Jun 14 '11 at 10:02
  • 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) – Meow Jul 26 '11 at 08:08
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.

Sheng Jiang 蒋晟
  • 15,125
  • 2
  • 28
  • 46
  • Thanks, but I'm using System.Windows.Form.HtmlElement and casting its instance to mshtml.IHTMLDOMNode is compile error. – Meow Jun 14 '11 at 07:35
  • I could do mshtml.IHTMLDOMNode castedElement = (mshtml.IHTMLDOMNode) elem.DomDocument; Console.WriteLine(castedElement.attributes); to cast but still gives me System.COM_Object. – Meow Jun 14 '11 at 08:39
  • 1
    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. – Sheng Jiang 蒋晟 Jun 14 '11 at 17:01
  • Is there any way to do this without enumerating all attributes (which is really slow) but directly using something `getAttribute` ? – dr. evil Nov 15 '11 at 13:13
  • I isolated this bug in IE - this fails `
    ` but this won't `
    ` 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")` >> `
    ` . Is this bug well known by IE team, or where to report MS Connect?
    – dr. evil Nov 15 '11 at 14:22
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;
        }
    }
}
madhu
  • 31
  • 1
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.

Faraz
  • 21
  • 1
2

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

Poma
  • 8,174
  • 18
  • 82
  • 144
  • Thanks for the info. The problem is though I need to click on the element with onclick (ex
    ) 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.
    – Meow Jun 07 '11 at 00:51
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.

Community
  • 1
  • 1
Dennis Röttger
  • 1,975
  • 6
  • 32
  • 51