Click here to Skip to main content
Click here to Skip to main content

An improvement to RegisterClientScriptBlock

By , 8 Jan 2004
 

Sample Image - scriptregister.gif

Introduction

One of the things that drove me nuts while writing web applications was the battle to modularise your code while still keeping generated HTML neat. In particular, having individual code modules within a page render their own javascript often resulted in pages containing many different and unrelated pieces of script scattered in no particular order. It's nice to have javascript appear within the HEAD tags of a page, where appropriate, but ASP 3.0 didn't provide an easy solution.

ASP.NET does provide a solution, of sorts, in the form of the RegisterClientScriptBlock method in the Page class. This allows you to register blocks of client-side script that will, at page render time, be output in one neat chunk to the page. I say it's a solution "of sorts" because while it's a neat idea, it doesn't actually do what you would always hope.

The issues, at least for me, of the RegisterClientScriptBlock method were that the script is output after the opening Form tag of an ASP.NET page. What if you don't have such a tag in your page? What if you want the script output in the HEAD section instead? The method also required you to include the <script> tags. To me just meant lots of repeated boiler plate code and potential for mistakes. I wanted something that would make it easy to include script from many different points within the page creation logic and from within controls contained by a page. I also wanted a method that would fill in the boiler plate code for me whereever possible.

A new base class for your webpages.

With ASP.NET providing the ability to derive your pages from whatever System.Web.UI.Page derived class you wish, there will no doubt be a plethora of web page base classes available. I could have chosen to encapsulate the functionality I needed in a separate utility class which would then be instantiated and called from your own System.Web.UI.Page derived class, but I'm lazy and so that particular exercise has been left to the reader.

I instead created a simple class, ScriptPage, derived from System.Web.UI.Page from which subsequent pages can be derived. The methods available are:

RegisterClientScriptBlock

Registers a client side javascript block. The block is automatically surrounded by <script> tags

void RegisterClientScriptBlock(string key, string script)
void RegisterClientScriptBlock(string key, string script, string language)
void RegisterClientScriptBlock(string key, string script, string language, 
                               bool defer)

Parameters

key - Script key. If a script file is already registered using this key then the previous script is replaced
script - The javascript to include.
language - The script's language. Default is "Javascript"
defer - Whether or not the script should be run after the page has fully loaded. Default is false

RegisterClientScriptFile

Registers a client side javascript file include

void RegisterClientScriptFile(string key, string file)
void RegisterClientScriptFile(string key, string file, string language)
void RegisterClientScriptFile(string key, string file, string language, 
                              bool defer)

Parameters

key - Script key. If a script file is already registered using this key then the previous script is replaced
file - The javascript include file to reference
language - The script's language. Default is "Javascript"
defer - Whether or not the script should be run after the page has fully loaded. Default is false

RegisterClientScriptEvent

void RegisterClientScriptEvent(string key, string eventName, string ctrlName,
                               string script)
void RegisterClientScriptEvent(string key, string eventName, string ctrlName, 
                               string script, string language)

Registers a client side javascript event handler. Will be rendered only for IE 4.0 and above.

Parameters

key - Script key. If a script file is already registered using this key then the previous script is replaced
eventName - The event to handle
ctrlName - The name of the HTML element(s) to apply this handler to
script - The script to run
language - The script's language. Default is "Javascript"

IsClientScriptRegistered

Returns true if the script block is registered; otherwise, false.

public bool IsClientScriptRegistered(string key)

Parameters

key - Script key to check.

Using the class

Using the class is simple.

  1. Include the ScriptPage.cs file in your project

  2. Derive your page from CodeProject.ScriptPage instead of System.Web.UI.Page.
    namespace CodeProject
    {
        /// <summary>
        /// Summary description for WebForm1.
        /// </summary>
        public class WebForm1 : CodeProject.ScriptPage
        {
            ...
  3. Include an ASP.NET Literal control named _clientScript at the place within your page where you want the script to be rendered. This can be anywhere on the page, but within the <HEAD> section is recommended.
    <HTML>
    <HEAD>
        <title>WebForm1</title>
        <asp:literal id="_clientScript" runat="server"></asp:literal>
    </HEAD>
    <body>
    
    <form id=Form1 ...

    Important: If you don't include this control then the javascript will not be rendered.

    Note: The designer in Visual Studio .NET will, if you edit your page in design mode, add the following line your code-behind class:

    protected System.Web.UI.WebControls.Literal _clientScript;

    If this occurs you will need to manually remove this line from your code-behind file.

  4. To register some javascript from within a page simply call the base class' methods:
    private void Page_Load(object sender, System.EventArgs e)
    {
        // Let's add some javascript
        RegisterClientScriptBlock("Script1", "alert(\"Script1 called\");");
        RegisterClientScriptFile("Script2", "MyScripts.js", "Javascript 1.2");
        RegisterClientScriptEvent("Script3", "onclick", "MyButton", 
                                  "alert(\"Script3 called\");");
    }
    
  5. To register some javascript from within a user or custom control simply cast the parent page to the base class and again call the base class' methods:
    CodeProject.ScriptPage basePage = Page as CodeProject.ScriptPage;
    if (basePage != null)
     basePage.RegisterClientScriptBlock("Script4", 
      "document.getElementById(\"MyTable\").style.backgroundColor='#ff9900';",
      "Javascript", true);

Results

Consider the page below which contains a simple form and a simple User Control. Both the form and the user control will register some javascript as described above.

<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" 
         AutoEventWireup="false" Inherits="CodeProject.WebForm1" 
         enableViewState="False" Trace="False"%>

<%@ Register TagPrefix="CP" TagName="WebUserControl1" 
    Src="WebUserControl1.ascx" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
   <title>WebForm1</title>
   <asp:literal id="_clientScript" runat="server"></asp:literal>
</HEAD>
<body>

<form id=Form1 method=post runat="server">

<p>This is a very simple webpage. Click 'View Source' to 
see the Javascript embedded within this page.

<P><input type=button id=MyButton name=MyButton value="Click me"></P>
 
<CP:WebUserControl1 id=WebUserControl11 runat="server"></CP:WebUserControl1>
 
</form>

</body>
</HTML>

The output, when rendered, is as follows:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>WebForm1</title>
<script type="text/javascript" language="Javascript 1.2" 
src="MyScripts.js">
</script>
<script type="text/javascript" language="Javascript"><!--
alert("Script1 called");
--></script>
<script type="text/javascript" language="Javascript" defer="true"><!--
document.getElementById("MyTable").style.backgroundColor = '#ff9900';
--></script>
<script type="text/javascript" language="Javascript" for="MyButton"
event="onclick" defer="true"><!--
alert("Script3 called");
//--></script>

</HEAD>
<body>

<form name="Form1" method="post" action="WebForm1.aspx" id="Form1">

<p>This is a very simple webpage. Click 'View Source' to 
see the Javascript embedded within this page.
 
<P><input type=button id=MyButton name=MyButton value="Click me"></P>

<table id=MyTable name=MyTable border=1 width=200 height=60 
       bgcolor=grey>
<tr><td align=center valign=middle><font size=2 face=verdana>This 
is a User Control</font></td></tr>
</table>

</form>

</body>
</HTML>

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Chris Maunder
Founder CodeProject
Canada Canada
Member
Chris is the Co-founder, Administrator, Architect, Chief Editor and Shameless Hack who wrote and runs The Code Project. He's been programming since 1988 while pretending to be, in various guises, an astrophysicist, mathematician, physicist, hydrologist, geomorphologist, defence intelligence researcher and then, when all that got a bit rough on the nerves, a web developer. He is a Microsoft Visual C++ MVP both globally and for Canada locally.
 
His programming experience includes C/C++, C#, SQL, MFC, ASP, ASP.NET, and far, far too much FORTRAN. He has worked on PocketPCs, AIX mainframes, Sun workstations, and a CRAY YMP C90 behemoth but finds notebooks take up less desk space.
 
He dodges, he weaves, and he never gets enough sleep. He is kind to small animals.
 
Chris was born and bred in Australia but splits his time between Toronto and Melbourne, depending on the weather. For relaxation he is into road cycling, snowboarding, rock climbing, and storm chasing.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralImprovement for ASP.NET 2.0memberMatthias Hertel29 Aug '05 - 4:06 
There is a trick available for ASP.NET 2.0 because the HTML heat element ist now marked with runat="server" and available as an HtmlHead object.
 
IsClientScriptBlockRegistered is used to check, if the given include is already part of the page.
RegisterClientScriptBlock then adds an empty text to the script collection that will be included inside a possible available form element, but has no bad effect there.
 
Then a literal with the real script reference is added to the controls inside the head element.
(You will not need to cast Page.Header to HtmlHead in the final ASP.NET version.)
 
// register the JavaScripts includes without need for a Form.
if (!Page.ClientScript.IsClientScriptBlockRegistered(Page.GetType(), "CommonBehaviour")) {
  Page.ClientScript.RegisterClientScriptBlock(Page.GetType(), "CommonBehaviour", String.Empty);
  ((HtmlHead)Page.Header).Controls.Add(new LiteralControl("<script type='text/javascript' src='"
    + Page.ResolveUrl("~/controls/cb.js")
    + "'><" + "/script>\n"));
} // if
 
This works with or without form element and you only need to modify only the part of existing controls that is registering scripts. There is no need for a literal with id="_clientScript" in the head so your pages need no update.
 
I use this in the AJAX controls for my AJAX engine. See http://ajaxaspects.blogspot.com
GeneralRe: Improvement for ASP.NET 2.0membermikeward0015 Mar '07 - 12:13 
I've been putting my scripts in the header section this way for a while now but never thought of using the empty string "trick" to register the script. Great tip! Smile | :)
GeneralRandom RegisterClientScriptBlock / RegisterStartupScript [Edit:Distro]memberEdbert P.16 Aug '05 - 14:51 
I had a javascript problem with my project where scripts were registered on random order.
I've found out that if I registered more than 9 scripts using either RegisterClientScriptBlock or RegisterStartupScript the scripts are ordered randomly and not based on the called order (see article[^]).
The problem seems to be caused by Hashtable being used for the scripts collection.
 
I haven't read Chris' source files so I'm not sure how this improvement is implemented, I'm just informing everyone to be aware of this.
 
[Edit] I've found another RegisterWhatever replacement called Distro[^]. From initial investigation it seems to use a literal control to register css & js to anywhere in the page. I've tested it and it's working but you have to apply the 'patch' written by Steve under the comments. [/Edit]
 
Edbert
GeneralCall it RegisterClientScriptFragmentmemberWalt Dell6 Jul '05 - 14:54 
I think it is a bad idea to fundamentally change a standard function of the base class while keeping the same prototype. If I forget that I'm using your class -- or, more likely, another developer edits my code -- we will end up with doubled script tags. I suggest you call your function "RegisterClientScriptFragment" instead of "RegisterClientScriptBlock" since your version behaves differently.
GeneralSmall bugmemberJon Sagara4 Jun '05 - 9:38 
Hi Chris,
 
I think I've found a small bug in RegisterClientScriptFile:
 
StringBuilder block = new StringBuilder();
block.Append("<script type=\"text/javascript\" language=\"");
block.Append(language);
block.Append("\" src=\"");
block.Append(file);   <-- If defer == true, the src element does not have a proper ending quotation mark.
if (defer) block.Append(" defer=\"true\"");
block.Append("\"></script>");
 
Thanks,
 
Jon
QuestionNo sources?sussgelde30 May '05 - 22:29 
Well, I didn't found the sources for ScriptRegister.dll . Where can I find them? Thx.
AnswerRe: No sources?memberkhmerkidnow3 Jul '05 - 13:50 
An improvement to RegisterClientScriptBlock
Can I get a copy of the DLL Source if you have them? thanks
GeneralUnregistring a ClientScript blockmemberMajid Shahabfar28 May '05 - 1:19 
Dear Chris,
What's a method for unregistring a registered clientscriptblock?
Thanks.
AnswerRe: Unregistring a ClientScript block [modified]memberguisardos15 Jan '09 - 10:38 
The RegisterClientScriptBlock use the key internally in the server, It doesnt appear in the client; so you cant identify the script block with that. So you cant remove it. Dead | X|
BUT!! OMG | :OMG:
The last parameter of RegisterClientScriptBlock lets you put your own tag. That tag can contain... lets say.. the ID! With this ID you cant remove it from the head element whenever you want. WTF | :WTF:
For example, I use this in some UpdatePanel:
ScriptManager.RegisterClientScriptBlock(UpdatePanel1, UpdatePanel1.GetType(), "MyScripts", _
"<script type="text/javascript" id="MyScripts">" & _
"........." & _
"removeDuplicatedScript('MyScripts');</script>" _
, False)
 
And I made an include to the page with this cool functions that I build:
// Include a javascript file inside another one.
function include(filename)
{
	var head = document.getElementsByTagName('head')[0];
	
    var scripts = document.getElementsByTagName('script');
    for(var x=0;x<scripts.length;>    {
        if (scripts[x].getAttribute('src'))
        {
            if(scripts[x].getAttribute('src').indexOf(filename) != -1)
            {
                head.removeChild(scripts[x]);
                break;
            }
        }
    }
	
	script = document.createElement('script');
	script.src = filename;
	script.type = 'text/javascript';
	head.appendChild(script)
}
 
// Removes duplicated scripts.
function removeDuplicatedScript(id)
{
    var count = 0;
	var head = document.getElementsByTagName('head')[0];
	
    var scripts = document.getElementsByTagName('script');
    var firstScript;
    for(var x=0;x<scripts.length;>    {
        if (scripts[x].getAttribute('id'))
        {
            if(scripts[x].getAttribute('id').indexOf(id) != -1)
            {
                if (count == 0)
                {
                    firstScript = scripts[x];
                    count++;
                }
                else
                {
                    head.removeChild(firstScript);
                    firstScript = scripts[x];
                    count = 1;
                }
            }
        }
    }
    clearAjaxNetJunk();
}
// Evoids the update panel auto generated scripts to grow to inifity. X-(
function clearAjaxNetJunk()
{
    var knowJunk = 'Sys.Application.add_init(function() {';
    var count = 0;
	var head = document.getElementsByTagName('head')[0];
	
    var scripts = document.getElementsByTagName('script');
    var firstScript;
    for(var x=0;x<scripts.length;>    {
        if (scripts[x].textContent)
        {
            if(scripts[x].textContent.indexOf(knowJunk) != -1)
            {
                if (count == 0)
                {
                    firstScript = scripts[x];
                    count++;
                }
                else
                {
                    head.removeChild(firstScript);
                    firstScript = scripts[x];
                    count = 1;
                }
            }
        }
    }
}
I cant find any real answer to this topic Mad | :mad: So I hope I could help someone!!!
Enjoy! Cool | :cool:
 
modified on Thursday, January 15, 2009 5:08 PM

GeneralGreat idea, but...memberAlexPKeaton15 Nov '04 - 5:27 
I read this article with great interest as it addresses a very specific issue I've had with ASP.NET. However, I also want the client-side script from built-in controls to render in the head tag as well. Specifically, I want the __doPostBack function to appear where it should instead of in the middle of my form. I assumed that by overriding the RegisterClientScriptBlock method the problem would be solved. Is there another method that needs to overridden, or does the form insert this script without registering it in any way?
 
-nb

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 9 Jan 2004
Article Copyright 2004 by Chris Maunder
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid