Dynamically Adding JavaScript onLoad Event-Driven Scripts To ASP.NET 2.0 Templated / Master Page Content Pages

Update, 17 May 2012: A better methodology for injecting JavaScript into ASP.NET master pages can be found here: http://dougv.com/2010/02/17/dynamically-adding-javascript-to-your-asp-net-master-page-from-a-child-page/

I absolutely adore the master pages feature in ASP.NET 2.0, which is basically a very advanced templating system.

But one of the biggest problems with master pages is that occasionally, you need to add JavaScript to just one or two pages, not every page that is subject to a master page / template. Even worse, some scripts need to fire on the onLoad event — and since it’s the master page, and not the content page, that contains the body tag, you need to somehow raise that event in the master page, from the content page.

I ran into this problem a few weeks ago when working on the MELMAC Education Foundation’s Web site, which uses the Google Maps API to provide a map and point-to-point driving directions.

It took me a while to resolve the issue, since it actually involved two seperate problems: How to implement the JavaScript on the page, and how to trigger the onLoad event, since with master pages, the tag isn’t in the content page itself.

Having found the answers, I thought I’d be a good netizen and share them with you.

Problem 1: Getting the JavaScript on the page

If you don’t care about well-formed XHTML, you can, of course, simply paste your JavaScript into the body of your page, whenever you need it. That’s bad form, however.

You could include the JavaScript in the head section of your master page. I find that a bit much, personally.

You could also create a JavaScript source file and link to it from the head section of your master page. In the end, that’s the solution I chose, since it’s programmatically the cleanest, and in the case of the MELMAC site, I don’t use much JavaScript, so loading it on every page is not a problem.

However, there’s a fourth solution that many ASP.NET developers use, which is technically OK. Basically, you create a ContentPlaceHolder in the head section of the page, then put your JavaScript in a properly nested Content area on your content page.

Thus, your master page’s head block would look something like this:

<head runat="server">
	<asp:ContentPlaceHolder ID="cphJS" runat="server" />
</head>

And in your content page, you’d just add your JavaScript function. For this illustration, the script will simply change the text of a div on the page.

<asp:Content id="cntJS" ContentPlaceHolderID="cphJS">
	<script type="text/javascript">
		function doItOnLoad() {
			var tagtowrite = document.getElementById("rewrite");
			tagtowrite.InnerHtml =  "<h1>Hello World!</h1>";
		}
	</script>
</asp:Content>

It’s worth noting that Microsoft does not recommend this methodology, and if you use Microsoft’s IDEs (e.g., Visual Web Developer, Visual Studio), they will issue a warning that ContentPlaceHolders are not allowed in the head section of a master page.

Even though the IDEs don’t like it, this method will work. Because Microsoft doesn’t endorse this methodology and the IDEs don’t like it, I chose not to add my JavaScript this way, but if you are using a lot of JavaScript and targeting specific scripts to specific pages, this is probably the wisest option.

Problem 2: Triggering the onLoad event from the content page

With the JavaScript on the page, we now need to trigger the onLoad event of the body tag.

There’s two good things about ASP.NET 2.0 that really work out for us here. The master page / content page system is one of classes and subclasses, so what we do on a content page can impact the master page; and ASP.NET allows us to treat any XHTML tag as an object, so we can programmatically change their attributes however and whenever we want.

First, we need to provide a specific ID to the body tag, and indicate that we want it to run at the server, the same way the head tag is set to run at the server by default when we create an ASP.NET 2.0 page in a Microsoft IDE.

So, our master page will now have a body tag such as this:

<body runat="server" id="htmBody">

Having assigned our body tag an ID and the runat=”server” attribute, we can now bring in the HTML namespace, which we’ll need if we’re going to programmatically change the tag. That namespace is System.Web.UI.HtmlControls. If you use the Microsoft IDEs, this library should be included, by default, in your web.config file. If not, you either can add the namespace to your web.config file’s namespaces section, or to the content page’s namespaces via the @Import directive.

To add the onLoad event to the body tag in our content page, we just need to add the attribute via the Page_Load subroutine on the content page. At compile time, ASP.NET makes a master class from the master page, and creates child classes from the content page; thus, anything we have on the master page, we can control from the content page!

So, we add this subroutine to our content page [The code below is in VB.NET].

This subroutine does the following:

  • Finds the body tag in the master page, using the very handy FindControl method, and the name we provided to the tag;
  • Adds the onload attribute to the body tag, and specifies the JavaScript function we want to have fire, using the very handy Attributes.Add method
Sub Page_Load(Sender As Object, E As EventArgs)
        Dim objBody As HtmlGenericControl = Page.Master.FindControl("htmBody")
        objBody.Attributes.Add("onload", "doItOnLoad();")
End Sub

Believe it or not, it’s that simple: Just point to your JavaScript using one of the methods above, name your master page’s body tag with an ID parameter, specify it to run at the server, and add an appropriately modified Page_Load subroutine, as above, to the content page.

Again, you can see this in action at the MELMAC Education Foundation’s Contact Us / Directions page.

If you found this helpful, or have questions, please leave a comment.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  • Check out the Commenting Guidelines before commenting, please!
  • Want to share code? Please put it into a GitHub Gist, CodePen or pastebin and link to that in your comment.
  • Just have a line or two of markup? Wrap them in an appropriate SyntaxHighlighter Evolved shortcode for your programming language, please!