Saturday, July 01, 2006

An alternative to Page.RegisterClientScriptBlock

When you make a custom control that requires custom javascript you usually have to make sure that
you output the javascript code once and once only. If you don't, the same javascript is downloaded to the browser and waste bandwith. There are also issues with global variables declared multiple times etc..

ASP.Net provides a Page.RegisterClientScriptBlock that allows you to register a piece of code once and the framework will output it once on the page. This works fine and I have been using it a lot.
The issue with this is that you must generate the javascript in code like this.

protected override void OnInit(EventArgs e)
    {
      string strCode = @"
         "<script language='javascript'>"
        +
        "function ChangeImage(ImageRef)"
        +
        "{"
        +
            "ImageRef.src = 'http://www.google.com/images/firefox/fox1.gif'"
        +
        "}"
        +
        "</script>";

      Page.RegisterClientScriptBlock("SomeUniqueKeyOfYourChoosing",strCode);
    }

This works fine but it would be cool if I could still put this code in the ascx and still make sure it is output
once. The advantage is that it is easier to edit ascx inline than editing the C# code to craft the javascript.

I came up with this simple solution. The idea is this. We need to keep track of the fact that we have or have not output the javascript section already. ViewState is out of the question since it is scoped to each control. SessionState is also out of the question. SessionState should be use sparingly and we should stay away from it if we can. We just need to keep track of the javascript for this page only so we can use the Items collection that lives only for the duration of the request. After we output the page, we do not need to keep count so this is a good solution.

We are half way there. We now need to not output the javascript. Here is the trick. We will wrap the script with a <div> tag with runat="server". Inside the <div> is the javascript code! We just need to control the visibility of the div!

Here is the complete control code. It is just an image that changes when you click on it. We call
a javascript ChangeImage() function in javascript when the image is clicked.

Cool <div> isn't it?

<%@ Control Language="C#" ClassName="SimpleControl" %>
<asp:Image runat="server" ID="_Image" />

<script runat="server">
    void Page_Load(object s, EventArgs e)
    {
        _Image.ImageUrl = "http://www.google.com/images/logo_sm.gif";
    }
    string JavascriptKey
    {
        get
        {
            return this.GetType().FullName + "JS";
        }
    }

    void HandleJavascriptCodeStreaming()
    {
        HttpContext ctx = HttpContext.Current;

        bool? JavascriptAlreadyOutput = (bool?) ctx.Items[JavascriptKey];

        JavascriptCode.Visible = !JavascriptAlreadyOutput.HasValue;
        ctx.Items[JavascriptKey] = true;
    }

    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        _Image.Attributes["onclick"] = "ChangeImage(this)";

        HandleJavascriptCodeStreaming();
    }
    
</script>

<div runat="server" id="JavascriptCode">
    <!-- We use the Div to Control The Visibility of the javascript code -->

    <script language="javascript">

        function ChangeImage(ImageRef)
        {
            ImageRef.src = "http://www.google.com/images/firefox/fox1.gif"
        }
    </script>

</div>

No comments: