ASP.Net has an interesting way of handing the potential ID conflicts caused by embedding third-party controls within your web-page; it prefixes any sub-controls with their parent’s ID.

<asp:TextBox ID="txtUsername" Runat="server" /> within a standard page simply has an ID of txtUsername within the HTML output. On the other hand, the following example would result in something along the lines of _parent_txtUsername as the ID:

<div id="parent" runat="server">
  <asp:TextBox ID="txtUsername" Runat="server" />
</div>

This isn’t really much of a problem when all you are working with is server-side code, but when you start tinkering with JavaScript, things become quite annoying and get an overall feeling of hackyness.

One solution, which I have used time-and-again, is to use a script block at the end of your page where you create a series of variables that contain the actual IDs. You then use these variables to reach the actual elements via JavaScript.

<script type="text/javascript">
  var txtUsernameID = '<%= txtUsername.ClientID %>';
  var txtPasswordID = '<%= txtPassword.ClientID %>';
</script>

This solution works fine, but it isn’t exactly pretty and it doesn’t weigh well on my conscience.

Javascript Mapping

I’ve created a simple class that traverses the control structure and outputs an object that contains the mappings, as above, but without any client-side intervention; not a <%= ...ClientID %> in sight.

An example of the output would be:
var ClientIDs = {
  txtUsername = 'ctrl0_txtUsername',
  txtPassword = 'ctrl0_txtPassword'
};

Which enables you to reference elements, that you know are server-controls, by calling ClientIDs.txtUsername.

Before:

<script type="text/javascript">
  var txtUsernameID = '<%= txtUsername.ClientID %>';
  var txtPasswordID = '<%= txtPassword.ClientID %>';


  function validate() {
    var username = $(txtUsernameID).value;
    var password = $(txtPasswordID).value;


    if (username == 'james') {
      alert("you shouldn't be here!");
    }
  }
</script>

After:

<script type="text/javascript">
  function validate() {
    var username = $(ClientIDs.txtUsername).value;
    var password = $(ClientIDs.txtPassword).value;


    if (username == 'james') {
      alert("you shouldn't be here!");
    }
  }
</script>

ServerControlIDs class

Methods

void Emit(page) – Builds the controls of the page into a mapped structure, registering a client-side script block for the output.

void Emit(control, literal) – Builds the controls of the control into a mapped structure, rendering the output into the literal control supplied. The literal is best suited to being placed into the <head> tag, which helps improve the semantic nature of your page.

Properties

bool Whitespace – Sets whether the output should contain whitespace, provided only for “prettifying” your code; defaults to off.

string VariableName – Sets the JavaScript variable name used; defaults to ClientID.

Usage

To use this class, simply create an instance in the PreRender event for your page — this ensures that all the controls have been set to their correct state — then call the Emit method you desire.

History

[1.1] Removed IDs from Repeater/DataGrid contained controls.
[1.0] Initial release

Download

That’s all there is to it, use the link below to get the latest version of ServerControlIDs.

Please note that this code isn’t pretty and it could probably do with some revising, but it works and that’s what’s important to me. I’m open to suggestions and feature requests.

Current Version (1.1)

Tags:

Comments...

  1. Your effort is much impressive.
    It is truely workable for ASP.NET 1.X.
    But as far as I concern, you might need to add some coding to exclude new repeater controls in ASP.NET 2.0.
    For instance, GridView,FormView,DetailsView etc.

    By Thurein — 8 Aug, 2007 @ 8:03 am

  2. You are correct, this was written before I’d started using .Net 2 my self. I should really update it, but in the meantime the source code is available.

    By James Gregory8 Aug, 2007 @ 9:20 am

  3. Hi,
    It seems your link to download the zip file is not working,
    Could you please fix the link to download the file

    By Tan6 Jun, 2008 @ 1:21 pm

  4. Greetings. I planned to download your project and save myself a bunch of time – but it’s no longer available. Is there an alternative download location? Thanks!

    By Rick — 6 Jun, 2008 @ 6:43 pm

  5. Thanks James, very helpful! Exactly what I was looking for. I’m using your scripted variable concept to pass a server control’s client id through a button’s OnClientClick event to a javascript in .Net 2 and it works like a charm. All other methods that I had tired thus far had failed in my given context for one reason or another.

    By Naz6 Jun, 2008 @ 8:55 pm

  6. Ian, Rick: I’ve fixed the download link.

    Naz: Glad you found it of some use!

    By James Gregory6 Jun, 2008 @ 7:40 am

  7. Hello James!!
    I left a comment a few days ago but i don’t see them =S
    Last time i visited your Blog, they say that they were waiting for moderation.
    Well I get back ina afew days =)
    Greetings!!!

    By KlimUser — 8 Aug, 2008 @ 9:18 pm

  8. James, et. al.,

    I thought that asp.net control IDs would be available in JavaScript (JS) in VS 2008, though they don’t seem to be (either that or I haven’t clued in to how this is achieved in VS08 without 3rd party help).

    Then I found this tool. Works great! I caution its use, perhaps to internal web apps, as it exposes too much of the code-behind in the ClientIDs array; e.g. actual asp.net control ID names.

    Also, ClientIDs array has *every* control in it, whether I use it in JS or not; I prefer only those controls which I want exposed in JS. I could hardcode something, use generally global IDs, or put an identifier in the .NET ID, such as “JAG” at the end of the controlID [a form of recognition for James' solution, per se].

    I first opted for putting JAG at the end of the .NET ID. I then modified ServerControlIDs.GetMappedIDs to only process those controls wherein the .NET ID ends with “_JAG”.

    This may work for the most part for most programmers; however, I was adding/removing this tag more than I wanted, so I implemented a more generic tag in the .NET ID that I already use.

    For years I’ve append a prefix to my .NET IDs, such as ‘txt’ for Textbox, ‘btn’ for Button, ‘chbx’ for checkbox, ‘ddl’ for DropDownList. You may see where I’m going with this. Rather than suffixing “_JAG” (sorry, James!!), I merely changed ServerControlIDs.GetMappedIDs to look for my already used prefixes at the beginning of the .NET ID. That way, I don’t have to add/remove “_JAG” (again, sorry James!) to the .NET ID.

    So, James, your code has made my life easier, yet more complex! I’m now doing double validation coding! client-side now (via leveraging your solution) and server-side (which, in reading other blogs, it seems to be a ‘best practice’ to be doing double-validation anyway . . . )

    Thanks, James.

    Cheers!

    GaryN

    By Gary Noter — 3 Mar, 2009 @ 3:24 pm

Post a comment...