Take Control Of Web Control ClientID Values in ASP.NET 4
By Scott Mitchell
Introduction
Each server-side Web control in an ASP.NET Web Forms application has an
ID
property that identifies the Web control and is the name by which the Web control is
accessed in the code-behind class. When rendered into HTML, the Web control turns its server-side ID
value into a client-side id
attribute. Ideally,
there would be a one-to-one correspondence between the value of the server-side ID
property and the generated client-side id
, but in reality things aren't
so simple. By default, the rendered client-side id
is formed by taking the Web control's ID
property and prefixed it with the ID
properties of its naming containers. In short, a Web control with an ID of txtName
can get rendered into an HTML element with a client-side id
like ctl00_MainContent_txtName
.
This default translation from the server-side ID
property value to the rendered client-side id
attribute can introduce challenges when trying to
access an HTML element via JavaScript, which is typically done by id
, as the page developer building the web page and writing the JavaScript does not know what
the id
value of the rendered Web control will be at design time. (The client-side id
value can be determined at runtime via the Web control's
ClientID
property.)
ASP.NET 4.0 affords page developers much greater flexibility in how Web controls render their ID
property into a client-side id
. This article
starts with an explanation as to why and how ASP.NET translates the server-side ID
value into the client-side id
value and then shows how to take
control of this process using ASP.NET 4.0. Read on to learn more!
ASP.NET 4.0's Status and Release Date |
---|
At the time of this writing, ASP.NET 4.0 is currently available a Release Candidate (RC). You can download the .NET Framework 4.0 and Visual Studio 2010 RC or wait until the software is officially released. Currently, ASP.NET 4.0 is scheduled to be released in April 2010. |
Why ASP.NET Modifies Client-Side id
Values
Each HTML element in a web page may contain an
id
attribute. The id
attribute uniquely identifies an element within the web page; no two HTML elements
should have the same id
attribute value. Commonly, the id
attribute is used to access an HTML element from JavaScript. For example, JavaScript's
document.getElementById(id)
function searches the DOM for an HTML element with an id
value of id and returns a reference to that element.
Web controls in ASP.NET also have ID
values that uniquely identify each Web control. At first blush it may seem that the ID
property values
of an ASP.NET Web control could be literally translated into the client-side id
value, since both must be unique. But keep in mind that several ASP.NET files
may be used in composing a single web page. For instance, when a browser requests the web page Default.aspx
, the ASP.NET engine may be composing the resulting
web page from a multitude of sources, including: a master page (Site.master
); a content page (Default.aspx
); and, perhaps, one or more User Controls.
It is certainly possible that a Web control in the master page could have the same ID
value as a Web control in the content page, or in a User Control. Consequently,
ASP.NET needs some mechanism to ensure that the client-side id
values it generates are unique for the entire web page.
The way this is accomplished is by translating the server-side ID
value into a client-side id
value using the following sequence of steps:
- Set the client-side
id
value to the name of the Web control's server-sideID
value. - Inspect the Web control's parent controls. For each naming container encountered, prefix the client-side
id
value with theID
property of the naming container.
id
rendered, create a simple ASP.NET site with a master page and a content page. Add a TextBox to the content page and set its server-side ID
property value
to something simple, like txtName
. Next, visit the page through a browser and do a View/Source. You'll see that the TextBox control's rendered client-side id
value is something like ctl00_ContentPlaceHolder1_txtName
. This is because the TextBox is within two naming containers: the ContentPlaceHolder control in the
content page (named ContentPlaceHolder1
, by default) and the master page itself (named ctl00
). The figure below illustrates the control hierarchy,
showing the naming containers in peach. (This figure is from the Control ID Naming in Content Pages
tutorial, which is one of ten tutorials in my Master Pages tutorials series.)

Most Web controls that display data or use templates operate as naming containers, as well. To understand why, consider a GridView that contains a TemplateField with a Label named
lblName
. The GridView renders this template once for each record bound to it, so if four records are bound to the GridView then there will be four Label controls
rendered in the page. Imagine what would happen if the GridView and its rows were not a naming container - the four Label controls would end up having the same id
attribute value.
To prevent this naming conflict the GridView and its rows are made naming containers, resulting in rendered id
attributes like:
GridViewID_ctl00_lblName
GridViewID_ctl01_lblName
GridViewID_ctl02_lblName
- ...
You can tell that the GridView is a naming container because its ID - GridViewID - appears in the rendered client-side id
attribute. The rows of the
GridView are also naming containers. They appear in the client-side id
with the auto-generated names ctl00
, ctl01
, and so on.
Retrieving the Client-Side id
Value At Runtime
At design time the page developer can compute the client-side
id
value that will be rendered by a Web control, but it is unwise to assume that that computed
id
value will always hold. If another developer changes the ID
s of one of the naming containers, or adds a new naming container somewhere in the
control hierarchy or removes an existing one, the client-side id
value rendered by a Web control will change. It is possible to determine the
client-side id
attribute value that will be rendered via the Web control's ClientID
property, and is typically used when crafting JavaScript that needs to reference a Web control.
The following JavaScript snippet references a textbox HTML element and sets focus to the element. The textbox in the web page was actually a TextBox Web control; to determine
the client-side id
attribute the TextBox control's ClientID
property is used.
<script type="text/javascript">
|
The syntax <%=TextBoxID.ClientID%>
emits the client-side id
value directly into the markup. The browser will be sent markup like so:
<script type="text/javascript">
|
Taking Control Of The Rendered Client-Side id
Value With ASP.NET 4.0
ASP.NET 4.0 grants page developers finer control over how a Web control renders its client-side
id
value via a new property,
ClientIDMode
.
The ClientIDMode
property is defined on the Control
class,
meaning that it applies to all Web controls in the ASP.NET Toolbox, including the Page
itself.
The ClientIDMode
property can be set to one of the following four values:
AutoID
- uses the same series of steps for computing the client-sideid
value as in previous versions of ASP.NET.Static
- the client-sideid
value is the same as the server-sideID
property value.Predictable
- used for controls with repeating templates, like the GridView or ListView. When selected, the client-sideid
value is concatenated with a specifiedClientIDRowSuffix
property, which is the name of a data field whose value is appended to the generated client-sideid
.Inherit
- specifies that the control's client-sideid
value be generated the same way as its parent.
ClientIDMode
property can be set at different levels of the application. You can set it at the Web control level like so:
<asp:Label ID="..." runat="server" ClientIDMode="..." />
|
If not specified, a Web control's default ClientIDMode
setting defaults to Inherit
.
Alternatively, you can set the default value for the ClientIDMode
property for all controls on a specific page via the page's @Page
directive
(or for all controls in a specific User Control via the User Control's @Control
directive):
<@ Page ClientIDMode="..." Title="" Language="C#" ... />
|
If not specified, the default ClientIDMode
for the page is AutoID
.
Or you can set the default value for the ClientIDMode
property for all controls on all pages using the <pages>
element in Web.config
:
<configuration>
|
The AutoID
and Inherit
settings are pretty straightforward, I think. More interesting are the Static
and Predictable
settings, which
we'll examine throughout the remainder of this article. The code that we'll be looking at is available for download at the end of this article.
Using the ClientIDMode
's Static
Setting
The
Static
setting works precisely as described: when set, there is a literal translation from the Web control's ID
property to the rendered HTML element's
id
attribute. Earlier in this article I gave an example of how ASP.NET renders the client-side id
for a TextBox control in a content page with its
ID
property set to txtName
. By default, ASP.NET will set the client-side id
to a value that includes the ID
s of the
control's naming containers. In this case, txtName
may get translated into something like ctl00_MainContent_txtName
.
The Static
setting ensures that the generated client-side id
attribute value is identical to the server-side ID
value. For instance,
were we to set the txtName
TextBox's ClientIDMode
property ot Static like so:
<asp:TextBox ID="txtName" runat="server" ClientIDMode="Static" />
|
We'd end up with rendered markup like so:
<input name="ctl00$ContentPlaceHolder1$TextBox2" type="text" id="txtName" />
|
Note that the client-side id
value mirrors the server-side ID
value. Note that while the client-side id
value has followed the naming
guidelines, the name
attribute remains a lengthy string that includes the naming containers ID
s. The reason is because on postback the browser sends
back the name/value pairs for the <input>
elements. ASP.NET needs to fully qualified name to be able to determine the TextBox in the ASP.NET page that the
submitted value belongs to.
When using the Static
setting you must take care to ensure that you are not creating HTML elements with identical id
attributes. In its
ASP.NET 4 and Visual Studio 2010 Web Development Overview white paper Microsoft points out:
It is up to you to make sure that the rendered controlID
s are unique. If they are not, it can break any functionality that requires uniqueID
s for individual HTML elements, such as the clientdocument.getElementById
function.
Using the ClientIDMode
's Predictable
Setting
When using the
AutoID
setting, any naming containers without an explicitly specified ID
value have an auto-generated ID
value computed and used
when crafting the client-side id
attribute. For example, the default behavior for crafting the id
attribute for a TextBox control in a content page
is to include the name of the master page. Since the master page doesn't have an ID
value explicitly specified, ASP.NET names it ctl00
since it is the first such
auto-named control. This results in a client-side id
like ctl00_ContentPlaceHolderID_TextBoxID
. The problem is that this client-side
id
isn't predictable. It can change if other naming containers without explicitly defined ID
s are added to the page. For instance, it might become
ctl01_MainContent_txtName
.
To better allow for predictable, yet unique client id
values, use the Predictable
setting. In its simplest incarnation, the Predictable
setting simply omits any auto-named naming container IDs when crafting the client-side id
. Returning to our txtName
example, when using the Predictable
setting the rendered client-side id
value would be MainContent_txtName
. (Note how the ctl00
prefix has been omitted.)
Recall that when rendering a templated control, like a GridView, the GridView control and each of its row operate as naming contains and are used to assign a unique id
attribute for each control in each rendered grid row. However, the GridView's rows' ID
s are not explicitly set, so they use auto-generated values like so:
GridViewID_ctl00_lblName
GridViewID_ctl01_lblName
GridViewID_ctl02_lblName
- ...
Static
setting these names would vanish, leaving us with the following client-side id
values:
GridViewID_lblName
GridViewID_lblName
GridViewID_lblName
- ...
id
names are no longer unique. To rectify this, the Predictable
setting automatically appends the index of the
row number to the client-side id
. Consequently, instead of rendering the above id
s, the Predictable
setting will generate the following:
GridViewID_lblName_0
GridViewID_lblName_1
GridViewID_lblName_2
- ...
ClientIDRowSuffix
property. Take the following GridView, which displays the categories in the Northwind database. Note that its ClientIDMode
is set to Predictable
and its ClientIDRowSuffix
is set to CategoryName
, one of the names of the data fields bound to the grid. Also note that this GridView contains a
Label Web control in a TemplateField named lblCategoryName
.
<asp:GridView ID="gvCategories" runat="server" AutoGenerateColumns="False"
|
When the above GridView is rendered, it will generate client-side id
s for the CategoryName
Label like so:
ContentPlaceHolder1_gvCategories_lblCategoryName_Beverages
ContentPlaceHolder1_gvCategories_lblCategoryName_Condiments
ContentPlaceHolder1_gvCategories_lblCategoryName_Confections
- ...
ID
s are omitted from the rendered client-side id
and how the name of the category is appended. Typically,
you'll set the ClientIDRowSuffix
to the primary key field(s) to ensure a unique client-side id
value. (In theory, two categories could have the same
CategoryName
value, in which case the rendered id
values on the page would not be unique.)
The above example shows the ClientIDRowSuffix
assigned to a single data field, but you can set this property to include multiple data fields - just separate each
one with a comma. When specifying multiple data fields each data field value is appended to the id
and separated by an underscore.
The Effect Of Parent Naming Containers When Using The Predictable
Setting
Bear in mind that the
Predictable
ClientIDMode
setting respects the ClientIDMode
setting of its parent naming containers. This behavior
can be used to establish a base from which the naming container is crafted. Imagine that we had a User Control that contained a GridView and that this User Control was added
to a number of content pages in an ASP.NET website. Using the AutoID
setting (the default), the client-side id
s generated by the controls in the
GridView would include all naming container ID
s, including auto-generated ones. This includes the master page, the ContentPlaceHolder, the User Control,
the GridView, and the GridView's rows, resulting in id
attributes like:
ctl00_ContentPlaceHolder1_UserControlID_GridViewID_ctl00_lblName
ctl00_ContentPlaceHolder1_UserControlID_GridViewID_ctl01_lblName
ctl00_ContentPlaceHolder1_UserControlID_GridViewID_ctl02_lblName
- ...
ClientIDMode
to Predictable
then the auto-generated ID
s would be removed and the row index or specified
data field value would be appended, resulting in id
s like the following:
ContentPlaceHolder1_UserControlID_GridViewID_lblName_0
ContentPlaceHolder1_UserControlID_GridViewID_lblName_1
ContentPlaceHolder1_UserControlID_GridViewID_lblName_2
- ...
ID
is still part of the rendered id
. We could omit this portion of the id
by setting the User Control's
ClientIDMode
to Static
, which can be done via the User Control's @Control
directive. This would result in id
s like so:
UserControlID_GridViewID_lblName_0
UserControlID_GridViewID_lblName_1
UserControlID_GridViewID_lblName_2
- ...
id
is crafted recursively. In short, the lblName
control says, "My client-side id
will be my ID
value prefixed with whatever my parent's client-side id
happens to be."
lblName
's parent does the same thing, as does its parent, and its parent, all the way up to the root. When the User Control's ClientIDMode
is set
to Static
, the recursion essentially stops at the User Control because the User Control no longer says, "Give me my parent's id
." Instead, it says,
"Hey, I'm set to Static
, I'll just use my own server-side ID
value as the client-side id
," thereby short-circuiting the recursion.
Also, keep in mind that this logic can be applied to any naming container, not just User Controls.
Conclusion
Since its inception, ASP.NET has not offered developers any control over the client-side
id
attribute values generated by its Web controls. The auto-generated
id
values are, at times, unnecessarily long. While the client-side id
value can be determined programmatically at runtime, there's no way to safely
know it at design time, which can complicate the JavaScript you need to write to access the rendered HTML element.
ASP.NET 4.0 gives Web Forms developers greater control over the id
values rendered by Web controls thanks to the new ClientIDMode
property. When set
to AutoID
, Web controls generate their client-side id
values just like they did in previous versions of ASP.NET. Setting it to Static
causes the Web control to emit a client-side id
value that's identical to its server-side ID
property value. The Predictable
setting
strips creates an id
attribute value that omits all auto-named naming containers and, for data Web controls, appends either the row index or the values of one or
more data fields as specified by the data Web control's ClientIDRowSuffix
property.
Happy Programming!
Attachments:
Further Readings: