Loading your JavaScript only when its needed

Ensuring that you're not loading any extraneous JavaScript files on a particular page can often be tricky. What's even more tricky is packaging the JavaScript in a modular within a Control Library so that it can work like a plugin.

Say What?! Yeah what I just said was practically incoherent. But, consider this example. You're building a Control Library that contains all of the custom controls you've built throughout your career. You now want to sell this library and make installation of it so easy that all you have to do is drop the DLL in the /bin folder and reference the controls in your markup. But there's a catch. Your controls use JavaScript files. Under normal circumstances you could handle this in 3 ways (I know, there are more than 3 ways to do this):
  • Include instructions with your library that tells developers to add the <script> references to the JavaScript files themselves.
  • Read the file, or generate the script using string concatenation and such (YUCK!! doing it this way is evil!) and making a call to ClientScriptManager.RegisterClientScriptBlock or ClientScriptManager.RegisterClientScriptInclude (which will place your code, inline, near the top of the <body>....uhhh....that's pretty shady).
  • Make your JavaScript file an embedded resource and make a call to ClientScriptManager.RegisterClientScriptResource (which will put the <script> reference near the top of the <body>).

The 3rd option is probably your best if you want to make installation for your customer as easy as possible. That's actually the way I would suggest it be done.

But that's all good and dandy until you realize that 9 out of 10 of your customers are experiencing "object does not exist" errors because they're mixing components and one control's script loads before another does or maybe because your code requires a JavaScript framework like jQuery to already be present and the customer is either not referencing it or is referencing it at the bottom of the <body> (like you're supposed to). Not only that, but some elite haxor bozo calls you a noob because your "crappy" code is slowing down their page load times by having the component scripts load before the markup (oh man...this is going to really hurt your feelings when it happens...and it'll happen at least once in your career).

So to remedy the problem of the jQuery requirement, you embed the minified jQuery v1.4.1 file as a resource in your library's DLL file and make the call to ClientScriptManager.RegisterClientScriptInclude. The RegisterClientScriptInclude method ensures that referencing it in every one of your custom controls doesn't load jQuery more than once. But, the method does not guarantee that it will be loaded before all your other references, even if you call the method for the jQuery resource before all the other scripts in your library. Oh, and not only that but 9 out of 10 of your customers are already loading jQuery themselves so your valiant effort to appease that 1 person out of 10 and load jQuery for them results in the framework being loaded more than once on every page load. Just great.....

But lets not shun the effort to have your controls install seemlessly or to load dependencies automatically. Afterall, both of these goals aim at complete modularity and componentization of your work. And that's important!

Finally, a solution to all of this poor developer's problems.

OK. OK. So enough ranting already. Here are the most common hardships that you'll face when trying to reference JavaScript in a modular control:
  • All of the "Register" methods of the ClientScriptManager class cause your scripts to be referenced near the top of the <body>, before all of the markup. This causes your page to load slower and increases the chance for dependency problems.
  • In an application that mixes JavaScript frameworks and control libraries of many sorts (i.e. in a CMS like DotNetNuke), the chance of duplicate script references is greatly increased.

These may not seem like a big problem, but when you have a client that see your DotNetNuke page is taking over 3 seconds to load because of all of its script references at the top of the body...well...you're screwed. In the end, the page will take the same amount of time to load even if the scripts were declared at the bottom of the <body>, but for the client to see the graphics appear after 1 second of load, rather than after 3 seconds, will save your ass.

The Script Manager

To help developers keep their components modular even when they're referencing JavaScript files, I created the Script Manager control. Yes, ASP.NET AJAX has one of these too but it's not the same. This one is lighter and works like you'd expect it to. (If the ASP.NET AJAX ScriptManager has never caused you severe problems before...then you're one of the lucky ones.) View the Script Manager component page for a detailed overview of the component and how it works and to see a few examples.

Best Practice
At the bottom of every one of your User Controls, or in the Page_Load of your custom WebControl, you should make register your JavaScript files with the InternalScriptManager of the AJAX Control Framework. If you have a User Control you can interact with the InternalScriptManager class through the use of the ScriptManager control that comes out-of-the-box with the AJAX Control Framework. Here's an example from the BingMapsSample project included in the source code:

Script Manager in markup

Notice in this example that I'm referencing the ucSearchResultsList.js file that is associated with the User Control at the bottom of the .ascx file. The <script> element for this reference will automatically be rendered at the bottom of the <body> for you, after the dependent jQuery script has been loaded. Also, a name of "ucSearchResultsList" has been assigned to it so that if any other scripts are referenced by the same name, duplicates will not be rendered.

To see how the InternalScriptManager can be used in your custom WebControls (which won't have a markup file associated with it) check out the Programmatic Example found in the Script Manager component's documentation page.

Oh and feel free to use the ScriptManager control in the markup of your Pages.

Important Note - If you choose to render your scripts at the bottom of the <body>, you have to include runat="server" in the start tag of the body element. The same concept applies if you want to render your scripts in the <head>, be sure to include runat="server" in the start tag of the head element.



Last edited May 25, 2010 at 9:49 AM by peanutbutterlou, version 8

Comments

No comments yet.