The Issue
In developing re-usable components such as ASP.NET User Controls (you know the files with the .ascx extension), Javascript is commonly used to achieve a richer, more responsive user experience. The INamingContainer interface fixes up client IDs and Names so that they are all unique on the client to avoid naming collisions. Javascript that accompanies this also needs to heed to this issue. Naming collisions can occur of a control is used multiple times on a page and thats precisely why the following methods (IsClientScriptBlockRegistered and RegisterClientScriptBlock) are used to avoid duplication of javascript functions that can accompany these controls as demonstrated below:
string scriptKey = “foo“;
if (!Page.IsClientScriptBlockRegistered(scriptKey))
{
// my javascript string created here
StringBuilder sb = new StringBuilder();
// remove the spacing between the letters, added in order to post blog
sb.Append(“< scr ipt lang uage='javascr ipt'>\n“);
sb.Append(“ function MyFoo(element)\n“);
sb.Append(“ {\n if (element) element.display=element.display!='block'?'block':'none';\n“);
sb.Append(“ }\n“);
sb.Append(“<“);
sb.Append(“/scr“);
sb.Append(“ipt/>“);
// Register script with Page
Page.RegisterClientScriptBlock(scriptKey, javascriptCode);
}
That is all fine and dandy, but what happens if you have a third party control - or even another one of your server controls that generates a function called myFoo as well? You are headed for problems for sure - and some frustrating debugging. To uniquely identify your javascript function name you'll have to generate your function names at runtime by prepending something unique - such as a namespace to it.
string scriptKey = “foo“;
if (!Page.IsClientScriptBlockRegistered(scriptKey))
{
// my javascript string created here
StringBuilder sb = new StringBuilder();
// remove the spacing between the letters, added in order to post blog
sb.Append(“< scr ipt lang uage='javascr ipt'>\n“);
sb.AppendFormat(“ function {0}(element) \n“, SafeFunctionName(this, “MyFoo“));
sb.Append(“{\n if (element) element.display=element.display!='block'?'block':'none';\n“);
sb.Append(“ }\n“);
sb.Append(“<“);
sb.Append(“/scr“);
sb.Append(“ipt>“);
// Register script with Page
Page.RegisterClientScriptBlock(scriptKey, javascriptCode);
}
What we can do is have a method that takes a class instances' name as well as the desired Javascript function name and create a unique javascript function name. You could also go further and get the Namespace of the class as well for absolute uniqueness, but for this example the class name is sufficient.
///
/// Creates a unique function name for a client javascript function
/// based on the class name it was created from as well as the
/// function name itself.
///
/// Object instance of owning class
/// Name of javascript function
/// String representing unique javascript function name
public static string SafeFunctionName(object instance, string functionName)
{
return String.Format(“{0}_{1}“, instance.GetType.Name(), functionName);
}
So in the example above, the function SafeFunctionName would return the name of the class as defined by the parameter instance and the desired Javascript function name and append them together to form a unique function name. Used in conjunction with IsClientScriptBlockRegistered, will yield a unique function name.
When assigning a server control to act on the function, you'll have to use SafeFunctionName to get the correct function name to call on the Client. For example in the statement below, if we have a Button object in our Server Control and we wish to call a javascript function called MyFoo as emitted by the same Server Control we would use the following statement.
Button1.Attributes[”onClick”] = String.Format(”javascript:{0}(this);”, SafeFunctionName(this, “MyFoo”));
In summary, prepending a unique identifier to the emitted javascript functions provides a means of guaranteeing uniqueness across multiple server controls to avoid naming collisions.
Have anyone else addressed this problem - and if so what were your approaches?