Monday, December 3, 2007

Masterpage – document.getElementById Issue

Masterpage is one of the good features of the ASP.NET 2.0. But if you are a beginner in .NET 2.0 and Masterpage, you would stuck up to get the element inside a Masterpage from a JavaScript code.


The issue was, whenever a control is inside a Masterpage the client id of the control would get append with the content placeholder id. So for an element of id “txtTest” the client id would be something like “ctl00_ContentPlaceHolder1_ txtTest”.


So when you use the document.getElementById(‘txtTest’), you will not get the access of the txtTest textbox in JavaScript. Indeed you need to access it by calling document.getElementById(‘ctl00_ContentPlaceHolder1_ txtTest’).


To avoid this we can access the control by using the document.getElementById('<%=txtTest.ClientID%>'). This will give you the access to txtTest text box. Now this will work fine until and unless the script is in line with the aspx page ie., if the script is included as a part of the aspx page. But the same won’t work if you have this script in a separate .js file, and add it to the aspx page.


So in this scenario to get the access to the control, we need to hard code the control’s id. But hard coding is not an ideal way of coding. To avoid this situation what we can do is to, maintain the mapping between the client id and the server id. This can be achieved as shown below.


First we need to declare two arrays and the first array will have the server ids of the required controls, and the second array will have the client ids of those server controls in the same order. Register these two arrays to the client side. Now create a JavaScript function, which will accept the server id and will compare the server id with the available server ids in the array and will give its position in the array. Then the same function would return you the matching client id in the same location of the client array. So we were able to get the mapping client id for the given server id.


The code below shows the declaration of the array and the declaration of the JavaScript function.


public void RenderJSArrayWithCliendIds(params Control[] wc)

{
if (wc.Length > 0)
{
StringBuilder arrClientIDValue = new StringBuilder();
StringBuilder arrServerIDValue = new StringBuilder();
// Get a ClientScriptManager reference from the Page class.
ClientScriptManager cs = Page.ClientScript;
for (int i = 0; i < wc.Length; i++)
{
arrClientIDValue.Append("\"" + wc[i].ClientID + "\",");
arrServerIDValue.Append("\"" + wc[i].ID + "\",");
}

cs.RegisterArrayDeclaration("MyClientID", arrClientIDValue.ToString().Remove (arrClientIDValue.ToString().Length - 1, 1));
cs.RegisterArrayDeclaration("MyServerID", arrServerIDValue.ToString().Remove(arrServerIDValue.ToString().Length - 1, 1));
cs.RegisterStartupScript(this.Page.GetType(), "key", "\nfunction GetClientId(serverId)\n{\nfor(i=0; i<MyServerID.length; i++)\n{\nif ( MyServerID[i] == serverId )\n{\nreturn MyClientID[i];\nbreak;\n}\n}\n} ", true);
}
}

We can make these codes as part of a common class for all the UI pages, so that in each page if need to access any one control in the JavaScript we can simply call the below function with the parameter as the controls to be accessed.


// Here give the coma seperated controls for which the client ids should be rendered
RenderJSArrayWithCliendIds(txtTest,lblTest);

This is one of the simplest ways to have a mapping between the client and the server id. By this way no need to worry about accessing a control inside a MasterPage from an external javascript files.

After this codes we can access the elements which are given in the RenderJSArrayWithCliendIds function in the javascript as shown below


// Get the text box
var txtTest = document.getElementById(GetClientId("txtTest "));


This will slove the issues arrised due to the change in the client id of the control which is placed inside a MasterPage.


Updates: I am not much experienced in VB, but still tried to translate the C# code to VB. Just check the below code.


Public Sub RenderJSArrayWithCliendIds(ByVal wc As Control())
If (wc.Length > 0) Then
Dim arrClientIDValue As StringBuilder = New StringBuilder()
Dim arrServerIDValue As StringBuilder = New StringBuilder()
Dim cs As ClientScriptManager = Page.ClientScript
Dim frm As HtmlForm = Page.Form
Dim i As Integer
For i = 0 To wc.Length
arrClientIDValue.Append("\"" + wc[i].ClientID + " \ ",")
arrServerIDValue.Append("\"" + wc[i].ID + " \ ",")
i = i + 1
Next
cs.RegisterArrayDeclaration("MyClientID", arrClientIDValue.ToString().Remove(arrClientIDValue.ToString().Length - 1, 1))
cs.RegisterArrayDeclaration("MyServerID", arrServerIDValue.ToString().Remove(arrServerIDValue.ToString().Length - 1, 1))
cs.RegisterStartupScript(Page.GetType(), "key", "\nfunction GetClientId(serverId)\nBegin\nfori=0; i<MyServerID.legth; i++)\nBegin\nif ( MyServerID[i] == serverId )\nBegin\nreturn MyClientI[i];\nbeak;\nEnd\nEnd\nEnd\n \nfunction GetObject(serverId)\nBegin\nfori=0; i<MyServerID.legth; i++)\nBegin\nif ( MyServerID[i] == serverId )\nBegin\nreturn document.all[MyClientIDi]];\nbeak;\nEnd\nEnd\nEnd\n", True)
End If
End Sub

10 comments:

Anonymous said...

Tx, that works for me!
That was hard work I guess...

Anonymous said...

May i know that any solution for vb?
I try to converting from your C# to vb.
But,i fail.
can give me a helping hand.
Thanks.

Rajganesh Mountbatton said...

I am not much experienced in VB, but still tried to translate the C# code to VB.

I have added it in the blog. Just check it.

Mohit said...

hi rajganesh
i was trying to use ur code in one common class file but i m geeting error in line ClientScriptManager cs = Page.ClientScript
An object reference is required for the nonstatic field, method, or property 'System.Web.UI.Page.ClientScript.get'
i m new to asp.net 2.0 so this code works fine in normal aspx page but not in class file.

Rajganesh Mountbatton said...

Hi Mohit,
I guess you are having the common class inside the App_Code folder. The issue was you need to inherit your common class from "System.Web.UI" class.
Considering your common class name as PageBase, then its declaration should be like

public class PageBase : System.Web.UI.Page

Anonymous said...

hi Rajganesh Mountbatton

thanx for ur concern.
it works thanx a lot atleast i was now able to build that project. but i now i was getting the javascript error GetClientId("txbox") not found.

Anonymous said...

hi Rajganesh Mountbatton

thanx for ur concern.
it works thanx a lot atleast i was now able to build that project. but i now i was getting the javascript error GetClientId("txbox") not found.

Regards
Mohit

Rajganesh Mountbatton said...

Hi Mohit,
Have you registered the "txbox" control, in its page load event?
If not, register it by RenderJSArrayWithCliendIds(txbox).
If, you are not getting it work, send me a workable copy, i will try it out.

Anonymous said...

Is there a working example? I tried following the steps but is not working.

Unknown said...

excellent anna iraga tisavu mana telugodi debba abba anali kummeyi nikenduku am kalyan i got some doubts as if am new to .NET 4 months so wants to contact u pls mail me if possible and time even