Question

I made some changes to an existing application that was previously just straight HTML and Javascript. To add server-side functionality, I chose ASP.NET, and took advantage of the Master Page concept. Unfortunately, on one huge web form, the control IDs are all mangled with the "ctl00$ContentBody$" prefix. I tacked a runat="server" on all the INPUT controls which is what altered the IDs. Now that the IDs have been changed, the Javascript (which is so poorly written I can't even read it, and I'm quite experienced with JS) is completely broken.

So, how can I prevent this nonsense from being rendered into the HTML? I was hoping to somehow be able to create a class that inherits HtmlGenericControl (I am not using Web controls, I just tacked on a runat="server" to every input tag) and somehow override the logic that sticks this "container id" at the beginning of the ID and NAME attributes. Then I could use tagMapping in web.config to make the global change. Is this possible?

Was it helpful?

Solution 6

Quick and dirty was what I needed, folks. And thanks to HBoss I was able to come up with a very simple solution. On the code-behind of the page in question, I just added this subroutine...

Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
    Dim Html As New StringWriter()
    Dim Render As New HtmlTextWriter(Html)
    MyBase.Render(Render)
    writer.Write(Html.ToString().Replace("name=""ctl00$ContentBody$", "name=""").Replace("id=""ctl00_ContentBody_", "id="""))
End Sub

OTHER TIPS

Small consolation for right now, but this "feature" will be fixed in Asp.Net 4.0. You will get settable ClientID property.

Other than that, the other responses are good.
Use <%= control.ClientID %>

or throw in JQuery and use $("[id$='myId'])

Or, on your input tags, don't put runtime='server'. You should be able to retrieve the values via the Form member (just like in traditional asp)

Or, skip Asp.Net WebForms and skip to Asp.Net MVC, which gives you complete control of the HTML markup that is generated. You have to change how you are doing things a bit more, but that might be less frustrating for you.

I tried this piece of code and for some reason it reorganises the entire page:

Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
    Dim Html As New StringWriter()
    Dim Render As New HtmlTextWriter(Html)
    MyBase.Render(Render)
    writer.Write(Html.ToString().Replace("name=""ctl00$ContentBody$", "name=""").Replace("id=""ctl00_ContentBody_", "id="""))
End Sub

If you wish to see the example ill submit the URL

I know of no way to remove the naming container from being prepended to the control id. In the past I've used a convenience method to look up such ids in javascript that iterates through the tag names and uses a regular expression to match the one that ends with $realID, where realID is the id that I gave to the control in mark up. Now, I would use jQuery for this using a selector like:

 $('[id$="realID"]')

Which basically does the same thing as my old convenience method, but it's built into the framework.

As far as I know there is no way to remove them for the very reason you said; to ensure they are unique. The way I see it you have two options:

  1. Create a JS variable and prepend it to all of your JS calls so it looks for the correct ID
  2. Use <%= control.ClientID %> in your javascript so it uses the new ID.

With #1 if any of your inputs are within any other server tag it won't work. This is prone to many problems so I would go with #2 if you can.

2 depends on either the javascript being on the page itself or if the JS is external you need to be able to pass in ID's. This code isn't tested and is only for an example but here is what I mean:

function alertValue(textID){
     alert(document.GetElementById(textID).value);
}

<input id="txtBox" runat="server" type="Button" text="Click Me" onClick="alertValue('<%= txtBox.ClientID %>');" />

It really depends how your JS is implemented.

Since it appears you're just trying to remove client ID's from the final output, you might be able to to override the render event on your page and use a Regular Expression to remove them. You can see an example in the post below.

Rewrite ASP.NET page output

After you get the text you're working with you could most likely write something like...

output = Regex.Replace(
    output, 
    @"id\=""ctl00\$[^\""]*""", 
    string.Empty, 
    RegexOptions.IgnoreCase
    );

And then write the final output to the HtmlTextWriter.

Hope that helps.

Check this: http://clientidless.codeplex.com/ - you can turn off ClientID to almost any control. Here is demo: http://show-demos.net/NoClientId/ - press "render without IDs" or "render with IDs" to see how page size changes. View source to see the difference.

I'd like to add my two cents to this one:

I did it this way (in the accepted answer), but thought overriding the Render control to write to a new TextWriter and then write the contents of that to the Page's OutputStream seems like a waste of resources to me. I worked out that this can be done by making a little class to override the HtmlTextWriter class. This way we write directly to the stream and we only try to replace the text when it is used as an attribute.

public class CleanHtmlTextWriter : HtmlTextWriter
{
    private const string MASTER_PAGE_ID = "ctl00";
    private const string CONTENT_NAME = "MainContent";

    public CleanHtmlTextWriter(TextWriter writer)
        : base(writer)
    {
    }

    public override void WriteAttribute(string name, string value)
    {
        if (name == "name")
            value = value.Replace(CONTENT_NAME + "$", "").Replace(MASTER_PAGE_ID + "$", "");

        if (name == "id")
            value = value.Replace(CONTENT_NAME + "_", "").Replace(MASTER_PAGE_ID + "_", "");

        base.WriteAttribute(name, value);
    }
}

Then you can use it like this:

protected override void Render(HtmlTextWriter writer)
{
    base.Render(new CleanHtmlTextWriter(writer));
}

Actually to piggy back on Josh's answer, I found the first solution below to be problematic with other input control elements and PostBacks not working correctly. So rather than making a blank change I opted for the second solution below to change the JavaScript rather than the element name.

Josh's Solution:

Code-Behind:

protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
    StringWriter Html = new StringWriter();
    HtmlTextWriter Render = new HtmlTextWriter(Html);
    base.Render(Render);
    writer.Write(Html.ToString()
        .Replace("name=\"ctl00$ContentBody$", "name=\"")
        .Replace("id=\"ctl00_ContentBody_", "id=\""));
}

Solution 1, with PostBack issues (NOTE: I could not change the ID which might resolve this solution):

Code-Behind:

protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
    var html = new StringWriter();
    var render = new HtmlTextWriter(html);
    base.Render(render);
    writer.Write(html.ToString()
        .Replace("name=\"ctl00$contentBody$rbl",
                 "name=\"ctl00_contentBody_rbl"));
}

Solution 2, to change JavaScript:

JavaScript in aspx file where Radio Button Lists use the convention 'rbl[name]':

if (!validateRadioButtonList("<% =rblTitle.ClientID %>"))

Code-Behind:

protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
    var html = new StringWriter();
    var render = new HtmlTextWriter(html);
    base.Render(render);
    writer.Write(html.ToString()
        .Replace("validateRadioButtonList(\"ctl00_contentBody_rbl", 
                 "validateRadioButtonList(\"ctl00$contentBody$rbl"));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top