Robust ASP.NET control referencing in JavaScript
AJAX, ASP.NET By Dave Ward on August 8th, 2007As you may know, ASP.NET controls often render with client-side IDs different than what’s declared in your aspx code. Especially when they’re nested in other controls (Tabs, Wizard, GridView, etc). When working with them on the server side this isn’t an issue, but tends to throw a monkey wrench into the gears of client-side development. Here’s an example:
<asp:Wizard runat="server" ID="Wizard1"> <WizardSteps> <asp:WizardStep> <asp:TextBox runat="server" id="TextBox1" /> </asp:WizardStep> <asp:WizardStep> <asp:Button runat="server" ID="Button1" /> </asp:WizardStep> </WizardSteps> </asp:Wizard>
TextBox1 and Button1 are accessible in code-behind via their declarative IDs, as we’d expect. However, trying to use $get() or getElementById() on TextBox1 or Button1 will fail. As it turns out, TextBox1 is rendered as Wizard1_TextBox1 and Button1 is rendered as Wizard1_Button1.

One solution is to hard code those rendered values in your JavaScript. $get(’Wizard1_TextBox1′) will correctly reference TextBox1 on the client side, but doing it that way is a maintenance nightmare. What would have otherwise been innocuous changes to the structure of your page could now break your client script. Not good.
There must be a better way!
Indeed there is. The better solution is to use inline ASP.NET code to inject the control’s ClientID property:
$get('<%= TextBox1.ClientID %>')
Now the correct client element ID is referenced, regardless of the structure of the page and the nesting level of the control.
In my opinion, the very slight performance cost of this method is well worth it to make your client scripting more resilient to change.
Similar posts
What do you think? Your comments are welcomed.
I appreciate all of your comments, questions, and other feedback, but please try to stay on topic. If you have a question unrelated to this post, I recommend posting on the ASP.NET forums. If you post there and then contact me with a link to the post, I'll try to take a look at it for you.
If you're replying to an existing comment, please use the threading feature. To do this, click the "Reply to this comment" link underneath the comment you're replying to.

Comments
Sorry, but I don’t quite get it. Could you please show a brief example in code?
Thanx.
For example:
I’ve got a similiar problem, but this doens’t work for me. I’m inside of an ascx control trying to reference an asp:textbox id. When I use the above code I get an error that states that “the name ‘CompletedDateTextBox’ does not exist in the current context.’ Any thoughts?
Your ASCX control needs to expose that property for it to be accessible at the page level. If it’s a user control that only has the TextBox, just give the user control a public ClientID property that returns the TextBox’s ClientID.
Yes that works, but only if on the .aspx page
What if we are separating out all our JavaScript in separate .js files, like everyone should be, for clear separation of view and controller?
Personally, I don’t mind a bit of light, presentation oriented JavaScript on the page.
I like to set up all of the heavy lifting in function libraries included off-page, but leave pageLoad() type initialization on-page (which is where I use this technique to pass the correct ClientIDs to the off-page JavaScript).
good idea … i usually create a global variable (ack!) that is basically the naming container for the nested control and reference the control like I would in normally. It works for smaller projects and I place it in a common.js file so I know where to look.
i.e.
var BASE = “cphMain_”;
function foo(){
var btn = $get(BASE+”btnSubmit”);
// do something interesting
}
That’s a good solution, but only if you’re not modifying the Page.Controls collection programatically from somewhere else in your server-side code. For example, if you try that on a page where you’re also doing something along the lines of Page.Controls.Add( new Label() ); or something like that, you’ll end up getting an error stating that the ClientID reference can’t be made because the Controls collection has been modified.
I’m quickly losing taste for the ASP.NET server controls model for this simple reason. You have to jump through some ridiculous hoops to write client-side code for an ASP.NET generated page. I wish there was a better way.
I also register a global variable to access the object. However I do a RegisterClientScriptBlock(…) and output all the variables through that method.
Exactly like Denny said: you do this:
RegisterClientScriptBlock(”var controlID = ‘” + Control.UniqueID + “‘)”;
Then you can keep your .js file separate.
For my money, it makes more sense to keep presentation related code in the aspx as much as possible. If you move it to code-behind, then that’s another step removed it is from a given JS include.
Too much JavaScript in code-behind always feels like coding business logic in stored procs to me.
Finally someone says what is the right way to do it.
Bad advice dude.
ClientID great until you need it in real world. You wouldnt even imagine the hacks I had to go through to just get the reference to the control that I wanted to call ClientID on. Iterating through a repeater just to find a control, just to find its client ID is absurd.
A superior workaround is to use CSS selectors in Javascript (mooTools, JQuery, and Prototype have them) to grab references to the runtime ids I need. Then its just about setting intelligent class names. May not be efficient on the client side, but it works. In ASP.NET as it stands now, I would recommend never ever referencing controls by ID.
What we need is a ClientID property on all controls that we can set manually or databind. So if I had a repeater with a bunch of buttons in it, each button could be bound to the ID of the corresponding database row. So I could say $(”button_5″) to grab the button.
I hate tying presentation to functionality.
I’ve done it when necessary (e.g. using jQuery plugins), but it’s not a very good long-term solution. Your design people shouldn’t need to understand client scripting and your personal conventions to work on a page’s presentation.
I kind of found another way to solve the problem, but it’s not necessarily the best nor most effecient way of doing it (but I thought I’d at least mention it).
http://www.ben-rush.net/blog/PermaLink.aspx?guid=70870288-191a-4dd0-a1a0-61365a26dea1
That’s an interesting way of handling it.
Keep in mind that using an arbitrary attribute won’t validate and will potentially be stripped out by the tools that designers use.
Yes, of course you’re absolutely right. However, in some circumstances where someone might be in a bind it might work - hard to say.
I think every solution is something of a hack (unfortunately).
hi i enjoyed the read
Hi Thanks a lot. This was nice and short. Thanks and It will sure make me to write good code…
Ewwww… :)
Here’s how I approach this challenge.
Most of the time I’m using a framework with DOM selectors anyway (my favorite being jQuery at the moment). So I simply supply an ID *AND* a NAME attribute for controls that I want to reference via Javascript. ASP.NET doesn’t modify the NAME attribute, so I can then use a simply jQuery selector to get the object I need from the page or external Javascript. For example, given the following HTML control:
Click me
I could then reference and modify it using jQuery like so:
$(’a[@name=ExternalLink]’).attr(”onclick”, “alert(’We are off to Google!’);”);
$(’a[@name=ExternalLink]’).html(’Click here to visit Google.’);
Of course, you’re not limited to using this NAME attribute method. There’s tons of ways to select objects with jQuery. Here’s an example using a class instead:
Click me
$(’a.ExternalLink’).attr(”onclick”, “alert(’We are off to Google!’);”);
$(’a.ExternalLink’).html(’Click here to visit Google.’);
that will only work for the controls that have a NAME attribute though. IMG control for example has no NAME attribute. What would you suggest doing in that case using JQuery?
Technically speaking, if you don’t care about stringent standards, any tag can have any attribute you want, so you could make up a whole new one ;)
I was mostly speaking from the perspective of getting form fields. Unfortunately though, I realized after I posted that in a repeater control, the name value is also modified by the .NET engine to match the id.
As I said above, you can also use class names to select against as well if you don’t want to use the name attribute. Not perfect, but it’s another option.
I tend to use tag-based selection (getElementByTagName()) and dom-walking to get to server control elements; I find it pretty reliable. If I need to, I can use staticall id’d html elements as anchors to get there more efficiently. But, in the button example in a repeater, I might wrap the whole repeater in a html div named something significant, then get a reference to that div, e.g. var div = $get(’repeaterDiv’);
then I can get a collection of buttons that are in the repeater div like:
var buttons = div.getElementsByTagName(’INPUT’);
for (var i=0;i
hmm… cut off some of my post there. Anyway, point is that you can iterate over that collection relatively easily at that point based on input type or the like, to make sur eyou’re only affecting what you want to be affecting.
Isn’t this entire problem caused because the HTML is generated on the server and not the client?
Shouldn’t the Ajax client be loading *data only* from the server and generating the HTML itself, ideally using client side implementations of extensible GUI widgets?
Then, when gui elements are created, they don’t even need Id’s, you would just store a refernce to them in javascript.
Something like this perhaps (an example OnHttpComplete method)
function onHttpComplete(result)
{
var grid = new myapp.controls.MyGridView(); // perhaps a styled subclass of a M$ provided Sys.WebForms.GridView
grid.dataSource = result;
grid.dataBind();
// if this was a class member:
this._grid = grid;
}
Unfortunatly we all seem to be heading off in the wrong direction and working round the server architecture of (classic postback style) asp.net.
One more thing, I just viewed one of the “how to” videos on doing “Pageflakes like” incremental page loads using asp.net ajax. Well, I couldn’t believe what I was seeing, Joe was actually proposing that you use an ajax call to download another aspx page and blat the entire thing (all HTML output from the aspx) into your page using the .innerHTML property of a div tag. He was delighting in how the page you bring down could have a server side Textbox in it and showed of how you could manipulate the content of it from posted variables! As if…as if that page could now be programmed like a regular aspx!!??
As a pattern, it’s a dead duck I’m afraid.
What I’m trying to say is that this is NOT how “real” ajax development is done, this is how asp.net developers are trying to emulate what they see on modern sites and I’m afraid itr looks like a hack from start to finish.
My advice would be to stick to the update panels as they are a genuine quick win, or invest in a gui library to generate your content on the client from the server data. Anything else and you are jumping through *asp.net induced* hoops in order to do even what should be simple stiff done.
I keep all my js in .js files, so I never use the $get. I just like to DOM walk.. It’s efficient enough I think. I pass the id and the tag of what is generated in the end.. ie (asp:Label = span). I also use a similar function for getting any gridview control I want.
function getDotNetCtrl(id,tag)
{
var arObj = document.getElementsByTagName(tag);
var serverCtrlName = id.replace(/_/g,’$');
var regExId = new RegExp(id+”$”, “ig”);
for (var i = 0; i < arObj.length; i++)
{
if (arObj[i].id)
{
if (arObj[i].id.match(regExId))
return arObj[i];
}
else if (arObj[i].name)
{
(arObj[i].name == serverCtrlName)
return arObj[i];
}
}
return false;
}
hi,
i have create the popup window. i want to click to ok button to value pass other form.
Ex:-
yahooMail. when i have click to compose button to display the compose form (here provide (To)button).when clicked to button to display the popup window. here i have select to id and clicked to ok EmailId display to (To Textbox).
how to do this type of code.
Thanks
I went through loops and wholes for this, but found a very very easy way to do it in jQuery:
$(”[id$=”]”)
Since ASP.NET controls IDs just get prepend with some values, they always end with the ASP.NET Control’s ID.
So this nifty solution will search for elements that have an ‘id’ attribute that ends with ”
Just my 2 cents…
What is the easiest way to get a control (in my case a dropdownlist) clientid that is in a gridview? I was trying something like this in one of the gridview template fields in the onclick event of a html input button.
Probably the best way is to do that during the RowDataBound event. You can use FindControl(”ddlUnderwriterUnassigned”).ClientID to find that row’s ddl’s ClientID. Then, use FindControl(”YourButton”).Attributes.Add to add your script to the button’s client side OnClick, with the ClientID you just found.
using clientID has one problem to find the html ‘id’ of asp.net server control. This problem last for many days for me and finally able to solve. But i don’t why this problem occured?
code i use like:
error message: code < %...%> something like this
I solved by keeping this script in
Am i right? if right, how it is justified?