Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to know which HTML object was clicked

0.00/5 (No votes)
9 Dec 2005 1  
This article shows how you can add JavaScript and VB code to detect what HTML element in an ASP.NET page was clicked by simulating an AutoPostBack.
Screen capture of the design window

Screen capture of the design window

Screen capture as it appears in a browser

Screen capture as it appears in a browser

Introduction

One of my programs had to visualize data that could not be returned by DB queries. I used a Table to display it. Later, the client wanted to be able to click on a cell to mark it, which had to be stored in the database. A TableCell doesn't have a server-side click-event, so I had to find another way.

The solution I was looking had to meet the following requirements:

  • It had to fire a server-side event that also provided the TableCell that was clicked.
  • It must also work if I was to add an AutoPostBack control in the future, without having to modify the code.

(Note: The solution proposed in this article can be used with any HTML element, not just TableCells).

What I tried, but didn't work

My first attempt had a form with:

  • Three HTML elements, which the user must click
  • A label to display the HTML element that was clicked
  • A hidden field to pass a value that would indicate which HTML element was clicked, and
  • A Submit button

To the three HTML elements, I added JavaScript code that would write a value in the hidden field and then simulate clicking the Submit button. This worked, but had one drawback: the Submit button had to be visible. If I made it invisible, a JavaScript error occurred, because ASP.NET had not included the invisible Submit button in the generated HTML and therefore it could not be found by the JavaScript code.

I then read the article Clickable Rows in a DataGrid by Dave Hurt. He used the __doPostBack function that is generated by ASP.NET to detect what DataGrid row was clicked. The idea was sound, but it only works if the webpage does contain a visible AutoPostBack control (TextBox, CheckBox or DropDownList with the AutoPostBack property set to true). I did not have, nor want, a visible AutoPostBack control on my page.

How postback works

Before I show you what I tried and did work, it is important to know how AutoPostBack works. Have a look at the HTML code that is generated when there is an AutoPostBack control:

<form name="Form1" method="post" action="WebForm1.aspx" id="Form1">
    <input type="hidden" name="__EVENTTARGET" value="" />
    <input type="hidden" name="__EVENTARGUMENT" value="" />
    <input type="hidden" name="__VIEWSTATE" value="dDwtODM1(...)+sg==" />

<script language="javascript" type="text/javascript">
<!--
    function __doPostBack(eventTarget, eventArgument) {
        var theform;
        if (window.navigator.appName.toLowerCase().indexOf("microsoft") > -1) {
            theform = document.Form1;
        }
        else {
            theform = document.forms["Form1"];
        }
        theform.__EVENTTARGET.value = eventTarget.split("$").join(":");
        theform.__EVENTARGUMENT.value = eventArgument;
        theform.submit();
    }
// -->
</script>

(...)

    <input name="TextBox1" type="text" 
       onchange="__doPostBack('TextBox1','')" 
       language="javascript" id="TextBox1" /></P>
</form>

A few things are important to notice:

  • ASP.NET added two hidden input controls: __EVENTTARGET and __EVENTARGUMENT.
  • It added a JavaScript function __doPostBack.
  • It added JavaScript to handle the client-side onChange event, which calls __doPostBack.

The basic idea

The answer to our problem has to both mimic this behavior and make it possible to mimic this behavior.

Postback uses the __doPostBack function and the hidden INPUT's __EVENTTARGET and __EVENTARGUMENTS. These are only generated if the WebForm contains an AutoPostBack control. If it does not, the __doPostBack method and the two hidden INPUTs must be created.

To know if elements are clicked, the WebForm must contain a button. The clickable HTML elements must do a postback as if that button was clicked. We can then handle that button's Click event on the server. To know *which* HTML element was clicked, we add a server-side hidden INPUT (runat="server") to our WebForm. When an HTML element is clicked, it can write a value that uniquely identifies it to this hidden INPUT. When we handle that button's Click event, the value of the hidden INPUT identifies the HTML element that was clicked.

Solution

The WebForm must contain:

  1. A JavaScript function elementClick(value), where value is a string that uniquely identifies the HTML element that was clicked. This function will write value to myHidden (see below) and mimic clicking btnSubmit (see below).
  2. Three HTML elements of which we want to know if it is clicked. Each element must call elementClick with a unique value when it is clicked (client side, that is). In my example, the HTML elements are TableCells with the texts "One", "Two", and "Three". They will call elementClick with the values "1", "2", and "3".
  3. An ASP:Label, called "lblResult", to show the HTML element that was clicked.
  4. A server-side hidden INPUT (runat="server"), called "myHidden", which we will use to pass the value to the server.
  5. A hidden ASP:Button, called "btnSubmit", to make it possible to capture a server-side event.

VB.NET: HasPostBacks(Form)

As said before, the hidden INPUT's __EVENTTARGET and __EVENTARGUMENT must be created if there is no AutoPostBack element in the WebForm. HasPostBacks checks a WebForm to see if it can find any control that has a AutoPostBack property set to True. It iterates through all the controls in the form, and it stops if it finds a control which posts back automatically.

The frameWork contains three controls that have an AutoPostBack property: CheckBox, TextBox, DropDownList. However, you could have created your own control that does post back automatically, and it needn't be a CheckBox, TextBox, or DropDownList. So checking if the control is a CheckBox, TextBox, or DropDownlist, and then checking if the AutoPostBackproperty is set, will not be a robust way of checking. I thought of only one thing when my solution will not work: when a User Control does post back automatically, but does not expose the AutoPostBack property. In order to catch all the controls that have the AutoPostBack property, I simply query the property. If it generates an error because that control does not have that property, obviously I does not post back automatically.

Protected Function HasPostBacks(ByVal Frm As HtmlForm) As Boolean
    Dim Ctrls As ControlCollection = Frm.Controls
    Dim i As Integer = 0
    Dim Found As Boolean = False
    While i < Ctrls.Count And Not Found
        Dim Ctrl As Object = Ctrls(i)
        Try
            Found = Ctrl.AutoPostBack
        Catch Ex As Exception
            Found = False
        End Try

        i += 1
    End While
    Return Found
End Function

VB.NET: EnsurePostBack()

Private Sub Page_Load(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles MyBase.Load
    'Put user code to initialize the page here
    EnsurePostBack()
    If Not Me.IsPostBack Then
        Table1.Rows(0).Cells(0).Attributes.Add("onClick", "elementClick('1')")
        Table1.Rows(0).Cells(1).Attributes.Add("onClick", "elementClick('2')")
        Table1.Rows(0).Cells(2).Attributes.Add("onClick", "elementClick('3')")
        TextBox1.Text = IIf(TextBox1.AutoPostBack, "PostBack = True", "PostBack = False")
    End If
End Sub

EnsurePostBack will add the hidden INPUTs __EVENTTARGET and __EVENTARGUMENTS if there are no AutoPostBack controls. First, it tries to find the Form object. With it, it uses HasPostBacks to determine if the hidden INPUTs must be added. If yes, it creates two HtmlInputHidden objects, sets their properties, and adds them to the Form.

Protected Sub EnsurePostBack()
    Dim Frm As Control = Me.FindControl("Form1")
    Dim HPB As Boolean = HasPostBacks(Frm)
    If Not HPB Then
        Dim EventTarget As HtmlInputHidden = New HtmlInputHidden()
        Dim EventArguments As HtmlInputHidden = New HtmlInputHidden()
        EventTarget.ID = "__EVENTTARGET"
        EventTarget.Name = "__EVENTTARGET"
        EventArguments.ID = "__EVENTARGUMENT"
        EventArguments.Name = "__EVENTARGUMENT"
        Frm.Controls.Add(EventTarget)
        Frm.Controls.Add(EventArguments)
    End If
End Sub

VB.NET: Page_Load

Every time the page loads, EnsurePostBack must be called. Because the controls are not part of the WebForm, they are not automatically recreated as controls that are part of the WebForm are.

In my case, I had a Table where I wanted to know which cells were clicked. So in my example, I have a Table with one TableRow with three TableCells. Because the designer doesn't allow to add attributes to a TableCell, I added the attributes in the Page.Load event. Because the TableCells are initially a part of the WebForm, the TableCells don't have to be added to the WebForm each time it is loaded. And because the TableCells are persistent, so are attributes of the TableCells. The attributes have to be added only once. "If Not Me.IsPostBack Then" makes sure it does happen only once.

The attribute names are "onClick", because we want to capture the client-side onClick event. The value of the arguments are a call to the JavaScript function elementClick, with a value of "1", "2", or "3".

Private Sub Page_Load(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles MyBase.Load
    'Put user code to initialize the page here
    EnsurePostBack()
    If Not Me.IsPostBack Then
        Table1.Rows(0).Cells(0).Attributes.Add("onClick", "elementClick('1')")
        Table1.Rows(0).Cells(1).Attributes.Add("onClick", "elementClick('2')")
        Table1.Rows(0).Cells(2).Attributes.Add("onClick", "elementClick('3')")
        TextBox1.Text = IIf(TextBox1.AutoPostBack, "PostBack = True", "PostBack = False")
    End If
End Sub

JavaScript: myPostBack(eventTarget)

It is only possible to do a postback if there is a __doPostBack method. This is only generated if the WebForm contains an AutoPostBack control. To do a postback anyway, there must be a function that does the same thing as __doPostBack, but has a different name. I call it myPostBack. Because it will never post back event arguments, I changed the code to:

function myPostBack(eventTarget) {
    var eventArgument;
    eventArgument = '';
    var theform;
    if (window.navigator.appName.toLowerCase().indexOf("microsoft") > -1) {
        theform = document.Form1;
    }
    else {
        theform = document.forms["Form1"];
    }
    
    theform.__EVENTTARGET.value = eventTarget.split("$").join(":");
    theform.__EVENTARGUMENT.value = eventArgument;
    theform.submit();
}

JavaScript: elementClick(value)

This method will be called by the clickable HTML elements. I use Microsoft's way of getting the form. If I have the form, I can get to the myHidden control, which will be assigned the value that was passed to this method. (Of course, it is also possible to use the document.getItemByID method, but I think this is neater.) Then, myPostBack will be called with the event-target 'btnSubmit', so that on the server-side, the btnSubmit.Click event will be fired.

function elementClick(value) {
    var theform;
    if (window.navigator.appName.toLowerCase().indexOf("microsoft") > -1) {
        theform = document.Form1;
    }
    else {
        theform = document.forms["Form1"];
    }
    
    var myHddn;
    myHddn = theform.myHidden;
    myHddn.value = value;
    
    myPostBack('btnSubmit');
}

Final words

I'd like to thank Dave Hurt for his article on Clickable Rows in a DataGrid. It helped me to find a solution for my problem. I hope my example can help you find your solution.

(If you want to try the example, start Visual Studio and create a new project called "HTMLElementClick". Download the source code and extract it to the project directory, overwriting the files Visual Studio created.)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here