ASP.NET 4.0 ClientID Overview

Published Tuesday, January 06, 2009 4:57 PM

Introduction

One of the new features being added to version 4.0 of ASP.NET is the ability to control the client side IDs that are generated by the framework.  Previously the framework would modify the client side IDs to uniquely identify each control.  This some times left you with the ID you defined in markup or sometimes left you with something that looks like this, “ctl00_MasterPageBody_ctl01_Textbox1.”

The Problem

The modification of the client side id property works great to ensure that each element is uniquely identified, however, to anyone that has tried to do any sort of client side scripting this becomes very frustrating. Chances are that if you have worked in ASP.NET for any time at all you have run into this issue.  The problem is that until runtime you do not what the client side ID could be, making it difficult to do any kind of client side scripting.  In addition any modification of the page, adding removing controls, can result in a different client side ID being generated.

Old Solution

Again if you have worked with ASP.NET for any amount of time you know there is a work around for this issue.  Each control has a property called ClientID that is a read only and supplies the unique client side ID.  You can use this in a code behind when dynamically adding scripts, or more commonly use inline code (old ASP style) to supply the value to and client side scripts.

<script type="text/javascript">
    function DoSomething(){
        alert('<%= Control.ClientID %>');
    }
</script>

ASP.NET 4.0 Solution

First off let me start by explaining why we decided to tackle this problem in version 4.0 of the framework.  While we provided a way of supplying the developer with the client side ID, with the growth of client side scripting this solution has become some what hacky.  There is not really a clean way to use this with lots of controls and lots of external script files.  Also it might have had something to do with the developer asking for control over this.  Developers do love to have control of everything, weather they use it or not, it’s just our nature :) The solution that we came up has four ‘modes’ that a user can use giving them everything from existing behavior to full control.  The controls ID property is modified according to the ClientIDMode mode and then used as the client side id.

Modes and what they do

There is now a new property on every control (this includes pages and master pages as they inherit from control) called ClientIDMode that is used to select the behavior of the client side ID.

<asp:Label ID="Label1" runat="server" ClientIDMode="[Mode Type]" />

The Mode Types

  • Legacy: The default value if ClientIDMode is not set anywhere in the control hierarchy.  This causes client side IDs to behave the way they did in version 2.0 (3.0 and 3.5 did not change this code path) of the framework. This mode will generate an ID similar to “ctl00_MasterPageBody_ctl01_Textbox1.”
  • Inherit: This is the default behavior for every control.  This looks to the controls parent to get its value for ClientIDMode.  You do not need to set this on every control as it is the default, this is used only when the ClientIDMode has been changed and the new desired behavior is to inherit from the controls parent.
  • Static: This mode does exactly what you think it would, it makes the client side ID static. Meaning that what you put for the ID is what will be used for the client side ID.  Warning, this means that if a static ClientIDMode is used in a repeating control the developer is responsible for ensuring client side ID uniqueness.
  • Predictable: This mode is used when the framework needs to ensure uniqueness but it needs to be done so in a predictable way.  The most common use for this mode is on databound controls.  The framework will traverse the control hierarchy prefixing the supplied ID with it’s parent control ID until it reaches a control in the hierarchy whose ClientIDMode is defined as static.  In the event that the control is placed inside a databound control a suffix with a value that identifies that instance will also be added to the supplied ID.  The ClientIDRowSuffix property is used to control the value that will be used as a suffix (see samples).  This mode will generate an ID similar to “Gridview1_Label1_0”

Samples

Legacy Mode

Legacy mode is pretty straight forward, it generates a client side ID the way that it had in version 2.0 of the framework.

markup:

<asp :TextBox ID ="txtEcho" runat ="server" Width ="65%" ClientIDMode ="Legacy" /> 

output:

<input id="ctl00_MasterPageBody_ctl00_txtEcho" style="width: 65%" 
name="ctl00$MasterPageBody$ctl00$txtEcho" />

Static Mode

Static is the most basic of all ClientIDMode modes, what you give for the ID is what you get for the client side ID. Once again a warning that if a static ClientIDMode is used inside of a repeated control it is the developer’s responsibility to ensure client side ID uniqueness.

markup:

<asp:TextBox ID="txtEcho2" runat="server" Width="65%" ClientIDMode="Static" />

output:

<input id="txtEcho2" style="width: 65%" name="ctl00$MasterPageBody$ctl00$txtEcho2" />

Predictable Mode

Predictable mode really tackles the heart of the problem.  The framework previously generated it’s unique IDs to prevent ID collisions and the most common place for these types of collisions are inside databound controls.  Predictable mode is really designed to work with databound controls but does not have to.  There is three ways to uses the predictable mode, each one of these is defined through the ClientIDRowSuffix property that specifies the suffix for each instance.  The ClientIDRowSuffix uses values from the control’s datakeys collection, so if the control does not have a datakeys collection this property is not viable.  If this property is not set or is not available the row index will be used in it’s place.

1. With no ClientIDRowSuffix defined, this is also the behavior for databound controls without a datakeys collection e.g. Repeater Control.  Notice that the framework has traversed the control hierarchy and prefixed the ID with the parent’s ID and suffixed the ID with row index.

markup:

<asp:GridView ID="EmployeesNoSuffix" runat="server" AutoGenerateColumns="false" 
ClientIDMode="Predictable" > <Columns> <asp:TemplateField HeaderText="ID"> <ItemTemplate> <asp:Label ID="EmployeeID" runat="server" Text='<%# Eval("ID") %>' /> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Name"> <ItemTemplate> <asp:Label ID="EmployeeName" runat="server" Text='<%# Eval("Name") %>' /> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>

output:

<table id="EmployeesNoSuffix" style="border-collapse: collapse" cellspacing="0" rules="all" border="1">
    <tbody>
        <tr>
            <th scope="col">ID</th>
            <th scope="col">Name</th>
        </tr>
        <tr>
            <td><span id="EmployeesNoSuffix_EmployeeID_0">1</span></td>
            <td><span id="EmployeesNoSuffix_EmployeeName_0">EmployeeName1</span></td>
        </tr>
        ...
        <tr>
            <td><span id="EmployeesNoSuffix_EmployeeID_8">9</span></td>
            <td><span id="EmployeesNoSuffix_EmployeeName_8">EmployeeName9</span></td>
        </tr>
    </tbody>
</table>

2. With a ClientIDRowSuffix defined, this looks in the control’s datakeys collection for the value and then suffixes the ID with that value.

markup:

<asp:GridView ID="EmployeesSuffix" runat="server" AutoGenerateColumns="false" 
ClientIDMode="Predictable" ClientIDRowSuffix="ID" > <Columns> <asp:TemplateField HeaderText="ID"> <ItemTemplate> <asp:Label ID="EmployeeID" runat="server" Text='<%# Eval("ID") %>' /> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Name"> <ItemTemplate> <asp:Label ID="EmployeeName" runat="server" Text='<%# Eval("Name") %>' /> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>

output:

<table id="EmployeesSuffix" style="border-collapse: collapse" cellspacing="0" rules="all" border="1">
    <tbody>
        <tr>
            <th scope="col">ID</th>
            <th scope="col">Name</th>
        </tr>
        <tr>
            <td><span id="EmployeesSuffix_EmployeeID_1">1</span></td>
            <td><span id="EmployeesSuffix_EmployeeName_1">EmployeeName1</span></td>
        </tr>
        ...
        <tr>
            <td><span id="EmployeesSuffix_EmployeeID_9">9</span></td>
            <td><span id="EmployeesSuffix_EmployeeName_9">EmployeeName9</span></td>
        </tr>
    </tbody>
</table>

3. With a ClientIDRowSuffix defined, but instead of just one value a compound value will be used.  Exhibits the same behavior as one value but it will suffix both values onto the ID.

markup:

<asp:GridView ID="EmployeesCompSuffix" runat="server" AutoGenerateColumns="false" 
ClientIDMode="Predictable" ClientIDRowSuffix="ID, Name" > <Columns> <asp:TemplateField HeaderText="ID"> <ItemTemplate> <asp:Label ID="EmployeeID" runat="server" Text='<%# Eval("ID") %>' /> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Name"> <ItemTemplate> <asp:Label ID="EmployeeName" runat="server" Text='<%# Eval("Name") %>' /> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>

output:

<table id="EmployeesCompSuffix" style="border-collapse: collapse" cellspacing="0" rules="all" border="1">
    <tbody>
        <tr>
            <th scope="col">ID</th>
            <th scope="col">Name</th>
        </tr>
        <tr>
            <td><span id="EmployeesCompSuffix_EmployeeID_1_EmployeeName1">1</span></td>
            <td><span id="EmployeesCompSuffix_EmployeeName_1_EmployeeName1">EmployeeName1</span></td>
        </tr>
        ...
        <tr>
            <td><span id="EmployeesCompSuffix_EmployeeID_9_EmployeeName9">9</span></td>
            <td><span id="EmployeesCompSuffix_EmployeeName_9_EmployeeName9">EmployeeName9</span></td>
        </tr>
    </tbody>
</table>

Summary

The ability to fully control the client side IDs that are generated by the framework is a request that has not generated much noise but everyone seems to want it when you mention it.  We believe that we have found a good solution to the request and think that it adds some much need functionality for developer that use lots of client side scripting.  There is an early preview and a walk through of this feature in CTP build that we released at PDC 2008.  For more information and a much more detailed description of this feature read Scott Galloway’s blog post.

by osbornm
Filed under:

Comments

# Web Development Community said on Wednesday, January 07, 2009 1:46 AM

You are voted (great) - Trackback from Web Development Community

# Guy Harwood said on Wednesday, January 07, 2009 3:18 AM

good writeup thanks

# Barry Dahlberg said on Wednesday, January 07, 2009 4:10 AM

Love it, love it.  Now we just need viewstate disabled by default and the world will be a better place.

# ASP.NET 4.0 ClientID Overview | Web Hosting and Domains said on Wednesday, January 07, 2009 5:53 AM

Pingback from  ASP.NET 4.0 ClientID Overview | Web Hosting and Domains

# Paul Cowan said on Wednesday, January 07, 2009 7:33 AM

Look how complicated ClientIDs are.

To think all this trouble for 1 attribute of an element.

I am shockedl

# osbornm said on Wednesday, January 07, 2009 1:16 PM

@Guy Harwood Thank you very much glad you liked it.

@Paul Cowan It becomes very complicated when you start dealing with repeated sections of markup, it was rather shocking to me as well but I think we have a good solution.

# Blackat.NET said on Wednesday, January 07, 2009 5:42 PM

Wow! I think it's not helpful only for Javascript code but for the weight of a page (in terms of bytes).

If I may, I would suggest  a "None" mode type (think a simple Label, a readonly textbox,ecc..) for rendering control without ClientID.

# Vipul Limbachiya said on Thursday, January 08, 2009 7:38 AM

Thts gr8!! I wanted it to be there from long time. Current method sucks, and its a real pain for javascript developers.

# Code Monkey Labs said on Sunday, January 11, 2009 11:59 PM

Pick of the week: Overnight Success – It Takes Years General Fun with Named Formats, String Parsing, &amp; Edge Cases : Just for fun, Phil Haack takes a look at custom string formatting and the challenges that come with it. Dictionary Attacks 101 : Jeff

# ASP.NET 4.0 ClientID Overview - Asp.Net QA Team said on Tuesday, January 13, 2009 12:30 PM

Pingback from  ASP.NET 4.0 ClientID Overview - Asp.Net QA Team

# ReTox said on Tuesday, January 13, 2009 4:02 PM

Do we really need name and id attribute? Isn't id enough?

And what about another mode: controls can incrementally get ID's as they are generated?

EmployeesNoSuffix_EmployeeID_0

becomes

C0

C1

..

..

CX

This way, markup will become much smaller, only 2 characters against 30+ ('EmployeesNoSuffix_EmployeeID_0)

# Scott Galloway said on Tuesday, January 13, 2009 5:19 PM

ReTox, the name attribute is used for postback data...this lets us hook up events / viewstate to the correct control. We did look at the ClientIdMode="None" idea (which would supress ID generation at all) but we'd want to get decent error checking for controls / script which DO need this so it probably won't make 4.0.

Paul_Cowan; yup, IDs are pretty complex...they are so complex as they perform a lot of the state preservation which underlies webforms (the name attribute hooks up to UniqueId which lets us associate posted data with a control in the page hierarchy).

# DotNetShoutout said on Friday, January 16, 2009 1:07 PM

Thank you for submitting this cool story - Trackback from DotNetShoutout

# Thomas goes .NET said on Monday, January 19, 2009 5:06 PM

The upcoming version if ASP.NET, which hasn't really changed in core since the release of ASP.NET 2.0 in 2005, will bring us a change in dealing with client identifiers of controls, resulting in a lot more flexibility, foremost for all developers wor

# Peter Bucher said on Monday, January 19, 2009 6:53 PM

Nachdem mich Alex unbewusst daran erinnert hat, das ich doch die PDC Aufnahmen endlich mal schauen will,

# Stuart Ballard said on Tuesday, March 17, 2009 10:08 PM

Is it possible to plug in a custom ClientID mode? I suppose you can use Static and manually set the ID for everything according to your own rule, but that isn't exactly what I meant.

It'd be nice to be able to implement our *own* version of "predictable" that let the ids come out rather shorter...

# AndrewSeven said on Wednesday, March 18, 2009 11:04 AM

This looks like it has some interesting potential, but what I, and the UI folks I work with, would really like is shorter ids and names in the html so that having a deep NamingContainer structure doesn't bloat the html.

Paulo Morgado has a posting with links to an implementation.

weblogs.asp.net/.../asp-net-futures-control-clientid-generation.aspx

# sandit27 said on Friday, April 24, 2009 4:25 AM

This is a much wanted feature in asp.net..

Thanks for making ClientID writable!

# web hosting nigeria said on Tuesday, May 19, 2009 2:16 PM

when will the said asp.net 4.0 be released. we are all looking forward to it.

# osbornm said on Wednesday, May 20, 2009 12:20 PM

ASP.NET 4 Beta 1 has just been released on MSDN

# Brian said on Sunday, May 24, 2009 5:42 PM

I think controls within data-bound controls should ignore a static setting at page level. Otherwise you are almost certain to have id clashes. They should default to predictable unless overrided specifically and regardless of page setting imo.

# Clint said on Thursday, July 16, 2009 12:19 PM

Finally!!!!! Oh, since ASP.NET came on the scene I've been fighting with this one.

# RAIS HUSSAIN said on Thursday, October 22, 2009 1:20 AM

Really a commentary feature which would play a vital role when working with jquery, and javascript.

# Crazy72 said on Thursday, October 22, 2009 8:05 AM

It could be that Afrigator is not the preferred blog listing for bloggers in these countries. ,

# Loy29 said on Friday, October 23, 2009 6:58 AM

Most importantly, I would like to emphsize that global warming is very much related to every other dire environmental consequence resulting from human lifestyles of overconsumption, pollution, and waste. ,

# Eric said on Monday, October 26, 2009 12:20 PM

A control adapter can also be used to strip off any prefixes and set the controls back to their original values. All this re-naming is incredible overkill, and all this engineering is over-engineering. I can keep the names in my applications unique; that isn't so hard. It is very hard working around my controls being renamed behind my back to solve a problem that I don't have.

# ASP.net said on Tuesday, December 01, 2009 1:22 AM

Μαζί με το .NET 4.0 έρχεται και η επόμενη έκδοση της ASP.NET. Πλέον με την έλευση του MVC υπάρχει ένας

# ASP.NET ClientID & jQuery small trick « Devdimi’s Software Development Log said on Monday, February 08, 2010 1:29 PM

Pingback from  ASP.NET ClientID &amp; jQuery small trick &laquo; Devdimi&#8217;s Software Development Log

Leave a Comment

(required) 
(required) 
(optional)
(required)