I have been using a class that extends WebControl to essentially do the same as RedSquare's suggestion. I read the suggestion on this blog
/// <summary>
/// Custom Web control that renders two JavaScript functions out into the page
/// so that defined control IDs can be used to get a reference to HTML DOM elements
/// </summary>
[DefaultProperty("Text")]
[ToolboxData("<{0}:ClientIdHandler runat=\"server\"></{0}:ClientIdHandler>")]
public class ClientIdHandler : WebControl
{
private StringBuilder _builder = new StringBuilder();
public ClientIdHandler(){}
/// <summary>
/// Renders the ClientID of the control in JavaScript function "clientId" to the specified writer.
/// </summary>
/// <param name="output">A <see cref="Control"/> whose ClientID will be included
/// in the rendered HTML output.</param>
public void AddControl(Control c)
{
_builder.AppendFormat(" case '{0}': return '{1}'; break;" + Environment.NewLine, c.ID, c.ClientID);
}
/// <summary>
/// Overrides the Render method to prevent a Start and End <see cref="HtmlTextWriterTag"/>
/// being rendered in the HTML for the control.
/// </summary>
/// <param name="output">A <see cref="HtmlTextWriter"/> that represents
/// the output stream to render HTML content on the client.</param>
protected override void Render(HtmlTextWriter writer)
{
RenderContents(writer);
}
/// <summary>
/// Renders the contents of the control to the specified writer.
/// </summary>
/// <param name="output">A <see cref="HtmlTextWriter"/> that represents
/// the output stream to render HTML content on the client.</param>
protected override void RenderContents(HtmlTextWriter output)
{
output.Write("<script type=\"text/javascript\"> " + Environment.NewLine);
output.Write("function clientId(id) {" + Environment.NewLine);
output.Write("switch (id) {" + Environment.NewLine);
output.Write(_builder.ToString());
output.Write(" }" + Environment.NewLine);
output.Write("}" + Environment.NewLine);
output.Write("function getElementByClientId(id) {" + Environment.NewLine);
output.Write(" return document.getElementById(clientId(id));" + Environment.NewLine);
output.Write("}" + Environment.NewLine);
output.Write("</script>" + Environment.NewLine);
}
}
Then you can use in aspx page or code-behind
protected override void OnInit(EventArgs e)
{
ClientIdHandler clientIds = new ClientIdHandler();
clientIds.AddControl(myControl);
this.Controls.Add(clientIds);
base.OnInit(e);
}
Which will render out the following in the page
<script type="text/javascript">
function clientId(id) {
switch (id) {
case 'myControl': return 'ctl00_Main_myControl'; break;
}
}
function getElementByClientId(id) {
return document.getElementById(clientId(id));
}
</script>
So in your external JavaScript file, you can use the following
// for jQuery selectors
var myControl = $('#' + clientId('myControl'));
// for vanilla JavaScript
var myControl = getElementByClientId('myControl'));