Embedding CSS and JavaScript in User Controls

Thursday, 14 January 2010 13:45 by Tom Gröger


User controls are among the better features that ASP.NET has to offer. I use them either whenever I realize that I can use a piece of code and markup somewhere else, but more frequently I write a User Control when things on a webpage are getting crowded and complicated and I want to split my code into smaller pieces.

Very often these "black box" User controls not only contain Html markup and Server code, but also JavaScript and CSS blocks. It is there when a seemingly simple task can get awfully complex - do you want your JavaScript in the Header or (better) in the bottom of the Page ? do you need <%= %> expressions to embed values like ClientID's into the script ? The same applies to CSS Style blocks, they should be placed in the Html Header in order to accelerate Page rendering. And sometimes you may also need expressions to embed WebResource-Urls or other values into the Styles Sheet.

Well, lots of articles have already been written to address this problems, none of them could truly convince me, until I came up with my own simple solution. Here is a short example:


<asp:Literal ID="DebugInfo" runat="server" Visible="false">
<link href="~/default.css" rel="stylesheet" type="text/css" />
<script src="C:\Eigene Dateien\Visual Studio 2008\scripts\jquery-debug.js"
type="text/javascript"></script>
</asp:Literal> <script runat="server"> protected override void OnPreRender(EventArgs e) { base.OnPreRender(e); // load ScriptBlock and replace ClientID's string src = ScriptBlock.Text.Replace( "LABEL_CLIENTID",
this.myLabel.ClientID); // move it to the bottom of our page this.Page.ClientScript.RegisterStartupScript( this.GetType(),
"MyScript", src ); // load CSS and move it into the Header src = Environment.NewLine + CSSBlock.Text; this.Page.Header.Controls.Add( new LiteralControl(src)); } </script>
<!-- Html Markup    -->      
<div id="MyDialog" style="display:none">
   
   <asp:Label ID="myLabel" runat="server"></asp:Label>

</div>


<!-- CSS Block    -->      
<asp:Literal id="CSSBlock" visible="false" runat="server">
    
   <style type="text/css">
      #MyDialog {width:750px; height:365px; }
   </style>
   
</asp:Literal>


<!-- Script Block    -->      
<asp:Literal id="ScriptBlock" visible="false" runat="server">

    <script type="text/javascript">
    
       $(document).ready(function() {
           var objLabel = $("#LABEL_CLIENTID");
           objLabel.html( "My User Control");
       });
    
    </script>
            
</asp:Literal>

 

The first thing that you have to do is to hide the CSS and Script Blocks in Server-Side Literal-Controls with a Visible=False attribute. This way our code is visible to ASP, but is not rendered into the Page:

<asp:Literal ID="DebugInfo" runat="server" Visible="false">
<link href="~/default.css" rel="stylesheet" type="text/css" />
<script src="C:\Eigene Dateien\Visual Studio 2008\scripts\jquery-debug.js"
type="text/javascript"></script>
</asp:Literal>

What happens here is that the ASP Parser finds two References: The first Link-Type points to my CSS File and so resolves all css class-definitions to avoid the dreaded Visual Studio “The class or CssClass value is not defined” warning. Because the Literal Block is invisible to the Page, the Stylesheet itself is not loaded on runtime. You can achieve the same result in one line if you do not need Javascript Intellisense:
 
<link href="~/default.css" rel="stylesheet" type="text/css" 
runat="server" visible="false" />

The second Script Type points to my jQuery VsDoc File. Since this Script is only used as a Reference for the ASP Intellisense Parser we can safely use a local path to the file. Voila – jQuery Intellisense in our User Control. 

Now lets take a look at the Javascript Block:
<asp:Literal id="ScriptBlock" visible="false" runat="server">
   <script type="text/javascript">

     $(document).ready(function() {
     var objLabel = $("#LABEL_CLIENTID");
     objLabel.html( "My User Control");
     });
   </script>
</asp:Literal>



The Javascript code is completely embedded into our ASP Server-Side LiteralControl. No need to mess around with encoding the </script> tag, just write and format your JavaScript Code as if you would do in a static html file. Note the LABEL_CLIENTID token, we will use that later to replace the token with a valid ClientID. Usually this kind of solution to overcome ASP.NET containership naming scheme issues uses code blocks like this :

var ctrl = document.getElementById("<%= myLabel.ClientID %>");


Fortunately we can’t use code blocks inside a Literal Control, so we are forced to use something more elegant than this ugly hack. So how do we get the Code into the Page ? This is preferably done in the Pages OnPreRender Event, which is launched as one of the last events during the creation of a page:

// load ScriptBlock and replace ClientID's 
string src = ScriptBlock.Text.Replace( "LABEL_CLIENTID", this.myLabel.ClientID);


// move it to the bottom of our page
this.Page.ClientScript.RegisterStartupScript( this.GetType(), "MyScript", src );


First we load the Content (= our Script ) of the Literal Control and replace the Token with the targeted Controls ClientID.  If you are using some kind of Script Compressor then this would be the right place to strip comments and white spaces etc. 
Next we use the pages ClientScriptManager to  insert our Script into the bottom of our page. If you prefer to insert it into the Header of your page, you can use the approach that we use to insert the CSS Block
:

// load CSS and move it into the Header
src = Environment.NewLine + CSSBlock.Text;
this.Page.Header.Controls.Add( new LiteralControl(src));

Same procedure here: Load the Css Code from the Literal Control, edit it to your heart’s content and then wrap it into a new LiteralControl to add it to the Page.Header Controls collection.


Using this technique you can easily write self contained User Controls with enclosed Scripts and Styles that embed themselves perfectly into the parent page, and as a bonus your get both CSS and JavaScript Intellisense for external files in your User Control …