Using JQuery to Make Asp.Net Play Nice with Asp.Net
I have found that developing an Asp.Net application that makes heavy use of javascript is very difficult. One of the major pain points is dealing with the horrendously long ids that Asp.Net server controls generates. For example, the following Asp.Net markup:
<asp:TextBox ID="myTextBox" runat="server" />
can cause the TextBox to be rendered as:
<input name="ctl00$ContentPlaceHolder1$myTextBox"
type="text" id="ctl00_ContentPlaceHolder1_myTextBox" />
if the page uses a MasterPage or any moderately complex control hierarchy. Asp.Net does this to minimize naming conflicts. This is the whole concept of a Naming Container. Asp.Net uses the interface INamingContainer to mark controls that can be rendered inside of a naming container. The end result is a really long id that can change based on the items place in the control hierarchy. This is a problem if you want to use the elements rendered by server controls in your client side javascript code.
First, the Hack
One technique for getting around this I have seen, has a definite bad code smell to it. You basically have Asp.Net render the itemsID inside your javascript code using the <% %> syntax like so:
<script type="text/javascript">
var myValue = document.getElementById('<%= myTextBox.ClientID %>');
</script>
this will render as
<script type="text/javascript">
var myValue = document.getElementById('ctl00_ContentPlaceHolder1_myTextBox');
</script>
This technique has a couple of huge shortcomings:
- It is really hard to maintain
- It doesn't support having your javascript in a library or a separte .js file
JQuery to the Rescue...sort of
There are many javascript client libraries out there that help ease the headache of writing javascript code that works across browsers. I am currently working on a project where we have decided to use JQuery which I am very impressed with.
One of the main features of using the JQuery library, is its use of selectors to find elements on the page. I will not get that deeply into this rich DOM querying language here, but you can read all about it from the JQuery Documentation.
JQuery selectors allow me to find items in the page many different ways. For example, I can find the textbox in my example above using the following code:
var myTextBox = $("input:text");
This will actually find every textbox on the page but since we have only on textbox on our page, it works. Alternatively, you can use something like the Css class of the item. Suppose we gave the textbox a css class of 'textInput':
<asp:TextBox ID="myTextBox" runat="server" CssClass="textInput" />
Now, the control renders as:
<input name="ctl00$ContentPlaceHolder1$myTextBox"
type="text" id="ctl00_ContentPlaceHolder1_myTextBox"
class="textInput" />
so we can use JQuery to select just this item using the following javascript
var myTextBox = $("input:text[@class=textInput]");
or
var myTextBox = $("input.textInput");
or even simpler
var myTextBox = $(".textInput");
There are many ways to select the same item, but you get the idea.
So does this solve our problem with long IDs? No, not really, we would still have to use the following javascript to select this item by id:
var myTextBox = $("#ctl00_ContentPlaceHolder1_myTextBox");
The Solution
As I was working through this problem in my head, I remembered somethign about javascript: it is more than just the attributes that already exist in DOM elements. You are not restricted to just using ID or Class. So why not just come up with your own attribute to use just for the purpose of selecting DOM elements? So here is a pretty workable solution I came up with:
<asp:TextBox ID="myTextBox" runat="server" ClientSelector="myTextBox" />
I just use a made up attribute called 'ClientSelector' (you can use whatever you fancy and it renders like this:
<input name="ctl00$ContentPlaceHolder1$myTextBox" type="text"
id="ctl00_ContentPlaceHolder1_myTextBox"
ClientSelector="myTextBox" />
so now we can use JQuery to select the item with this statement
var myTextBox = $("input[@ClientSelector=myTextBox]");
So now we can use the same selector for just this one textbox no matter if it is in a MasterPage or not. We don't have to care how the id renders anymore. I have found this technique very useful. What do you think? Is this also too much of a hack?