Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Inclusion of JavaScript Files


:P
On this page:

I've been thinking a bit about JavaScript inclusion in the last few weeks as I've been working on quite a few different Web projects that use a fair bit of JavaScript functionality. One thing that I'm still trying to feel good about is how JavaScript is included into pages. There are a number of different ways that script embedding can be done especially with ASP.NET and none of them are really perfect.

So what's the Problem?

The issue is plain and simple, how do you deal with JavaScript inclusion into the page given that you have a fair number of JavaScript files that are bound to change frequently and across multiple projects/applications? My assumption here is that I'm using a few libraries and utility classes both public (jQuery and jQuery plug-ins mainly) and some internally developed support libraries. All in all many pages I deal with have typically 4 - 8 related JavaScript resources in AJAX centric applications with about 15-20 JavaScript resources overall involved in a typical project. In addition there are a few ASP.NET custom controls that depend on some of these  Ajax libraries and components.

My project load currently runs about 10 concurrent projects both internal and external and all of them are making use of these same script resources. Keeping versions of both my internal JavaScript libraries as well as external libraries in sync and up to date for each of these applications both in development and online gets to be challenging especially if the files are not necessarily delivered as a unified whole (ie. often I pick and choose which plug-ins to use).

So as you can probably see, it's pretty easy to be in 'script file hell' - I find myself using Beyond Compare quite frequently to compare what's changed across different projects and update scripts precariously. I don't think there are easy solutions to this problem other than being very organized and managing versions as anally as possible but doing so can be a real drag on productivity as I move from project to project wondering if I have the correct current version.

There are many different ways that JavaScript resources can be loaded into pages from the basic manually managed header embedding of each script to using full ASP.NET resource management of all script files used. And plenty of other options in between. Try as I might I have not found a solution that really makes me feel all warm and fuzzy, so I'm curious what you are doing and what works best for you.

But let's review some of the approaches that are available to script embedding. For a specific example, let's take the more generic case of the jQuery library. Here are some of the options you have to get jQuery into your page:

  • Include in a scripts folder and link via script include as jQuery.js
  • Include in a scripts folder and link via script include as jQuery-1.2.6.js (ie. version specific)
  • Link to the version specific file at jQuery.com (typically jQuery-1.2.6.js)
  • Link to the latest version at jQuery.com (ie. jQuery-latest.js)
  • Link to some other script hosting site (ie. Google's Ajax Lib API)  via script loading
  • Embed scripts into ASP.NET Resources
  • Use ScriptManager to load scripts either from Folder or Resources
  • Use a custom script manager

All of these are options but they all have a few shortcomings as well.

Local Script Files

The most common and easiest to understand approach is to simply embed local script links into the page. Add scripts into the header with <script src="scripts/jquery.js"> and off you go. In many situations this approach is perfectly fine, especially if script files are static and don't change frequently.

<head runat="server">
    <title>Configuration Settings</title>
    <link href="Standard.css" rel="stylesheet" type="text/css" />
    <script src="scripts/jquery.js" type="text/javascript"></script>
</head>

The issue with locally stored script files is that they they need to be updated if files changed. In a single application this is not a big issue. But if you're managing a large number of projects, changes to resusable scripts both of your or third party script libraries can easily become a burden and an exercise in version management. It'd be great if source control would solve this problem, but because resources are often mixed and matched storing a fixed set of script files under source control also doesn't really address this scenario either and wouldn't work very well anyway if the project is already under Source Control.

In addition, if  you start using script code as part of related ASP.NET controls that might build on top of client script components  and that code has some dependencies on specific versions you can run into subtle versioning problems that are difficult to detect and test for. But even for relatively simple things like the static core jQuery library this can get sticky as new versions are frequently distributed.

Versioning JavaScript files in general is a sticky issue. If you have local files, how do you version these JS resource? For example, many jQuery examples are shown using the version specific file like so:

<script src="~/scripts/jquery-1.2.6.js" type="text/javascript"></script>

This addresses versioning alright but then gets tedious when a new version gets released and all pages referencing this version needs to be updated. Again, with a single file this isn't terrible but now think about 10-20 resources used in an application that are all versioned this not really a good option. The other choice is to use a non-versioned file and just update the files - this is easier for updates but in turn can result in subtle feature incompatibilities if newer esions break old builds/dependencies.

I personally lean towards the latter approach of using non-versioned filenames and take my chances hoping that changes are backwards compatible. In my own libraries even a breaking change is easily caught because I know what was changed intimately, but with third party libraries this can be tricky. For example, while using the jQuery.ui beta versions there many subtle breaking changes that were difficult to catch once the code was migrated to the release version because 95% of the code worked great. Testing JavaScript code consistently - especially UI code - is difficult at best and it's easy to have a subtle bug to not show up until the app is in production for a while.

One Library Path for All Scripts

Another option for 'local' script files might be to store all scripts in one place - say off the root of the Web site similar to the way ASP.NET 1.1 used to store resources in a /WebClient folder - and access them through this single script location. This solves the problem of having to copy versioned files around for each application and having a single store to update third party libraries and modify internal libraries. If there's a single store it's also easier to put the files under source control and manage them effectively.

<script src="/scripts/jquery.js" type="text/javascript"></script>

But this approach also has a few problems. First it's not a generic solution because it requires that you use the same convention across applications. Might work fine for your own applications where you set policy, but if you're working to somebody else specs it may not be possible to dump scripts into a specific consistent location.

It also may not necessarily be Ok to update scripts for every application at once, especially if scripts have breaking changes that might not work with older code not adapted to it. If you work with script files that are dependent on one another and these files are updated out of band it's easy to run into version problems. This is always a concern and not having local copies or linking to a single non-versioned script file is prone to potential version issues. Having local files in each app allows isolation from this version issue that can crop up with a single shared library folder. This issue is probably not a major concern

I played around with this approach some time ago, but tossed the idea because of issues

External Hosting

Another centralized approach is to use external hosting for certain script resources. This is more applicable for public librarires like jQuery, Prototype etc. and some high profile plug-ins available for them. External hosting also offloads the bandwidth for downloading scripts from your site to whatever public site hosting the scripts. Additionally, chances are that these script resources may be already cached in user browsers as the public resources are more widely used by users in general browsing which provides additional performance gains.

For example, you can load the latest jQuery version from:

<script src="http://www.jquery.com/src/jquery-latest.js" type="text/javascript"></script>

or a version specific one from the Google jQuery code repository:

<script src="http://jqueryjs.googlecode.com/files/jquery-1.2.6.js" type="text/javascript"></script>

Most other major libraries also provide similar direct links to libraries so that you can offload loading from your site.

Google recently also started hosting various JavaScript libraries with the Google's Ajax Lib API. This API allows you to load script via JavaScript code in the page and allows  you to specify specific version numbers as part of the API call. This is  a clever way to get a better handle on versioning as you can potentially use single script file to in your app to load various script files with 'factory' function calls that load each library with the appropriate version. FWIW, it doesn't require Google's API to do this; you can take a similar approach by yourself by creating a small loader script that can be updated for each application (see below on how this can be done with jQuery for example).

The disadvantage for external hosting is of course that you are dependent on an external provider to be up and running 100% of the time and to have adequate bandwidth to serve the script files.The provider can decide any time to remove files or APIs so at that point you may end up with bad links in your code. This is where an API - whether your own or Google's can actually buy some advantage because it can be changed in one place.

Another concern is that scripts loaded across sites can also trigger warnings in the browser in some situations like SSL connections (to non SSL script resources) or strict browser requirements for cross domain loading of any files.

Dynamic Loading 

As mentioned above you can also load script dynamically from other script code. For example, it might be useful to load one script from another script to provide dependency loading. In jQuery for example you can do:

/// <reference path="jquery.js" />
$.getScript("scripts/basepage.js");

to pull in additional script code.

One potential advantage of this approach is that you can build a simple loading api that turns scripts into resources with maybe some constants that determine which scripts to load and from where, consistently across an application. Rather than linking all scripts in pages you can call a single script file that handles loading all other scripts.

However, when loading script dynamically  it's important to be very careful and ensure that the code in the dependency is not accessed before the script has actually loaded. This can have tricky side effects. In one app I was working on just today pages would call into  a loaded script function and it would work just fine the first time the page loaded or when a full refresh occurred, but not when the page was just reloaded via click (ie. most resources are already cached). The difference is that the other scripts are fully cached and so load time is significantly faster and the code executes before the second script page loads. In this case IE fails to see the function on page startup (even in $(document).ready() in jQuery) while FireFox, Opera and Safari properly delay the loading code.

Most JavaScript libraries allow working around this via events fired when the script loading completes. In jQuery there's a handler that can be passed as a second parameter that's a handler:

$.getScript("scripts/wwJquery.js",function() {
    showStatus("Ready");
});

which will reliably cause dependent code to run. But it's not always feasible to tie code to a specific library having been loaded. Often you have to wait on multiple libraries. Oddly I've seen a host of problems with this approach both with jQuery, the Google API while script loading from the page's <script> tags seems to work just fine. Detecting script completion across browsers is tricky especially in IE it appears.

I have this approach on my list of things to play around with someday, but given the mixed results I've seen with load order failures I'm incline to pass this option up completely.

ASP.NET Resources

When using ASP.NET it's also possible to rein in JavaScript resources by building them into ASP.NET compiled assemblies as Resources. Using this approach allows the resources to be stored in a single location that is centrally managed. Web Applications can then access these resources by calling Page.ClientScript.RegisterClientScriptInclude(), Page.ClientScript.RegisterClientScriptResource()  or the methods of the same name on the ASP.NET ScriptManager control.

When using resources in a separate non-Web project, the project can be added other solutions and so can be shared across multiple projects which can both read the resources and modify any of them and have the changes reflected in any other application that includes the resource (or more often control ) assembly.

I use ASP.NET resources a lot for a variety of purposes from images to scripts to css files and it's a great way to put everything into one place. But resources are not without their problems either. The worst issue by far is that they produce hideously long URLs that are non-transparent in the page and you can't easily tell what each script file points at. They also take up a fair bit of space (a typical WebResource.axd url can be 150 chars long). Additionally the API to actually load these script files is a code only API that needs to be handled in Codebehind of the page with no HTML page markup equivalent.

Resources are a must if you're building controls that have script dependencies in my opinion and that's how I've been using them. I use my own script library that has both client and server components and the server controls need to ensure that scripts are loaded appropriately into the page. Without resources these controls would have to explicitly require the developer to add scripts to the page which would be a bit hokey. So the control internally loads the appropriate resources itself and in the right order and so ensures that the component is self contained.

On the flip side using resources this way can be a problem if you also use the same library for other coding. The smaller problem is that it's very easy to double load resources when you forget that a component loads the resources internally. Further it's possible that the component loads the resources too late or in the wrong order from what the same page code might require if there are dependencies.

In worse cases there may be issues where there are load order problems where the page control order rather than manual ordering end up determining the order in which scripts load. Again, this can become tricky if you have libraries that depend on each other like say a jQuery and a jQuery plug-in, or my personal library that might depend also on jQuery in certain parts.

Resources also don't work well with Intellisense, except when using them with the ScriptManager control (see below). Otherwise the resource URLs aren't transparent so there's no easy way to embed scripts into other JavaScript resources as /// <script reference="" />. 

The big problem is that ASP.NET natively doesn't have a native script loading API that works both for markup and code based script injection. Because the ClientScriptManager is a code only component it's very difficult to coordinate script loading both through markup and CodeBehind or component code and it can get very tricky to coordinate script dependencies loaded from resources if those same scripts also need to be loaded for other code.

ASP.NET Script Manager

Ah, but what about the new ScriptManager control that's part of Microsoft ASP.NET AJAX? It addresses a number of the issues that I've mentioned above. Indeed, ScriptManager provides a mirror interface for most of the ClientScript functions so you get the same functionality as the ClientScript for code manipulation and you get the actual control which can be stuck into a page and provide markup based script injection into the page.  You can embed both physical url based scripts links as well as resource links so it's possible to consistently embed scripts from both. The control also detects script duplication and only embeds only a single script reference into the page so it's easier for Markup and Code injected scripts to not generate multiple script references. It's possible to add a script like jQuery into the page and ensure that it doesn't get embedded twice for example.

But ScriptManager is also very problematic. First it's a .NET 3.5 component so it's really and add-on and not universally available. There's also no easy way to detect whether ScriptManager is available in a page without using the ScriptManager in the first place (it's possible but it's a royal pain). ScriptManager also requires a control on the page - it's not a standalone API which complicates using it in generic solutions especially since only one ScriptManager is allowed per page. Finally last but not least the control arrogantly throws the Microsoft ASP.NET AJAX ClientLibraries into the page (at least 28k of compressed script for minmal support) with no option to turn that off. If you're not using ASP.NET AJAX like myself the last thing I want is to get a bunch of scripts I'm not using thrown into my page.

Custom Script Manager

My first thought when seeing ScriptManager's issue of AJAX script loading was to subclass the control. It's easy to turn off the AJAX script injection, but unfortunately ScriptManager can't be subclassed in such a way that it still provides Intellisense because apparently Visual Studio's Intellisense for script Intellisense is hard coded to look for ScriptManager's specific properties. Script Intellisense is quite useful so ontop of the other shortcomings of SM I decided to just build my own.

The control I ended up with is similar in concept, but with better focus on externally hosted scripts.

<ww:wwScriptContainer ID="scripts" runat="server" RenderMode="Header">
    <Scripts>           
        <Script Src="Scripts/jquery.js"  Resource="jquery"></Script>                           
        <Script Src="Scripts/ui.core.js"  AllowMinScript="true"></Script>  
        <Script Src="Scripts/ui.draggable.js"  ></Script>                                   
        <Script Src="Scripts/wwscriptlibrary.js"  Resource="wwscriptlibrary"></Script>                           
        <Script Resource="ControlResources.Menu.js" ResourceControl="txtInput" />
    </Scripts>
</ww:wwScriptContainer>

The control can either be embedded into the page or there's a .Current property that can be used from code  that either finds an existing instance or creates a new control and adds it to the page dynamically which is useful for controls that want to embed resources. This gives a single API both for markup and code based embedding.

While working on this there were are a few other things that came up as useful: Optional automatic usage of minimized scripts (.min.js) extensions, abillity to embed all scripts into the page header (or inline, or as scripts) rather than adding into the page, the ability to order scripts by inserting at the end or beginning, as well as determining on a per script basis where it renders (Header, Script, Inline or InheritedFromControl) all of which gives some additional control both for markup and code behind. You can also embed resources but use reference a script on disk (for example in a central Web location) in order to still get Intellisense. The latter isn't as nice as ScriptManager's ability to just work off resource Urls, but at least it allows for some Intellisense of resource based scripts.

The format of the control is a bit ugly - I had to use the <scripts> and <script> tags in order to get Intellisense in pages to work because this is the only way I could figure out how to get Intellisense to work. Using custom prefixes (required for new controls) immediately breaks VS JavaScript Intellisense. The result is that I had to use HtmlControls for the script which means there's no Intellisense. Bummer, but the syntax is easy enough to look up.

This is a relatively new control for me and so far I've only used it with one project, but so far this looks promising. But of course this too is a non-standard way of working - it requires using this control instead of script embedding into headers, but at least the syntax of this control is pretty much based on standard Script tags. In fact, for basic scripts the syntax is identical (ie. like the draggable.js) above. If this sounds interesting to you let me know in comments and I can post more info on this control.

But this control is also not a generic solution. It works well for my internal purposes as well as for a few customers I'm working with but it's not totally satisfying either because of the inconsistencies in Visual Studio. And forcing developers to use a custom control is not my idea of a good design either, but for me at least this control addresses a few key scenarios like script compression and programmatic control over scripts and resources.

No Clear Winning Solution

So as you can see I have no clear answers that satisfy even my own requirements entirely. For the most part I'm still embedding scripts straight into the HTML header and hope for the best. The biggest issues I've run into have been conflicts between some of my controls that rely on script resources and scripts loaded through the header with duplicate loads. Most of this is mitigated by my controls which can disable script loading and defer to manually loaded scripts when needed (ie. all script resources are properties and can be loaded either from resources, a url or not all injected by the control). Tracking these things down though can be time consuming. I'm not there yet but I think using hte above control is likely to fix that problem, but for generic controls requiring this control container to be used is probably a hard sell.

Even the wwScriptContainer control  doesn't address the versioning and update scenarios although it does help with managing resource and file based script injection both for markup and controls (at least for my own controls). So I'm still searching for sanity and some input on what works for others who are heavily using scripts.

So, dear reader, what are your thoughts on this issue? Are you also fighting with script management hell? Do you use scripts from raw files, or do you use resources? What about for controls that use these scripts? And how do you manage these scripts if they get used across solutions? There are lots of options available but which ones make the most sense to you and why?

I'd love to hear your thoughts.

Posted in HTML  JavaScript  jQuery  

The Voices of Reason


 

Chris Shepherd
July 07, 2008

# re: Inclusion of JavaScript Files

My personal method for addressing this has been creating common libraries using Apache or IIS's ability to use server-side includes. I just create an include processor association and content-type configuration for .sjs similar to how .shtml is setup, and then I create files like:
core.sjs
ads.sjs
etc.sjs
late.sjs

The reason I use a new extension is simply to avoid having every .js file being parsed by the webserver. These files include whatever javascript files I need in the particular module, and then in the browser I would throw together a simple page such as:
<html>
<head>
<script type="text/javascript" language="JavaScript" src="js/core.sjs"></script>
</head>
<body>
[... content here ...]
</body>
</html>


Then inside core.sjs:
<!--#include file="jquery-1.2.6.packed.js"-->
<!--#include file="jquery-ui-1.5.1.min.js"-->


This is obviously very similar to creating a custom script manager, and in this particular case all the scripts will be appear to be inside core.sjs and not in their own script files.

This way I can allow pages to dictate which modules they need access to, and then internally in the .sjs files I can manage versions myself. It's not a perfect solution, and the projects I've applied this to thus far are fairly small, so there may be issues (I'm thinking performance-wise) with it I've not run into yet.
The one nice thing for me is that this has the added bonus of working regardless of using a particular language since it's a webserver feature.

Igor Loginov
July 07, 2008

# re: Inclusion of JavaScript Files

Hi Rick,

I ended with custom .ashx handlers for javascript and css. My includes look like:

<link rel="stylesheet" type="text/css" href="Css.ashx?path=~/css/main.css,~/css/style2.css" />
<script type="text/javascript" src="Js.ashx?path=~/js/jquery-1.2.6.pack.js"></script>


This approach doesn't solve version or min/pack problem, but definitely allows me:
1) more flexible manage "file" or "resource" scenario basing on query string or config,
2) force usage of server-side cache (and ETag for client-side caching),
3) compress or remove white spaces on the fly,
4) combine (!!!) several files in one request/response roundtrip, like for css above.

I took the idea from Mads Kristensen blog (see this for example http://blog.madskristensen.dk/post/Combine-multiple-stylesheets-at-runtime.aspx or all his modules and handlers http://blog.madskristensen.dk/page/ASPNET-HttpModules-and-HttpHandlers.aspx ) and slightly extended it.

DotNetKicks.com
July 07, 2008

# Inclusion of JavaScript Files

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Aaron
July 07, 2008

# re: Inclusion of JavaScript Files

For versioning, I created a custom class that writes the files lastModifedDate into the url. Then I have an HttpHandler that processes .js and .css files looking for that lastModifiedDate in the request url, parses it out, and returns the proper file while setting a far future expires header to take advantage of browser caching. This way if I update the file, the url gets updated, and the browser re-requestes it automatically.

Rick Strahl
July 07, 2008

# re: Inclusion of JavaScript Files

@Igor - I had toyed around with using Handlers, but decided against that particular approach as it breaks Intellisense and is non-natural to write out for designers. The syntax has to be familiar for designers to deal with ie. stay pretty close to the original script syntax or at least be Intellisense supported.

If I'm serving files from disk anyway I'd also just as soon avoid having ASP.NET process the file in any way and let IIS handle the caching (which it does very well) and the compression (which it does not so well). What's missing is the minifying or packing (minifying + gzipping is actually the smallest size usually as packed code doesn't compress as well).

Still it's interesting to think about ways to automatically provide the .min.js files either as part of the build process or maybe even as part of the control's first access - if the file doesn't exist create it. <shrug>

Dave Ward
July 07, 2008

# re: Inclusion of JavaScript Files

I haven't had any trouble yet, just using static files. To deal with caching issues, I include them with a version number in the querystring.

I've been including jQuery through the Google CDN, but without using their loader, like:

http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js

I'm generally very reluctant to use any runtime solution like the ScriptManager or HttpHandlers. I find that they tend to introduce complexity AND degrade performance.

Rick Strahl
July 07, 2008

# re: Inclusion of JavaScript Files

@Dave - so how do you deal with a version update for jQuery in that case? Rename all linked refs in batch?

Dave Ward
July 07, 2008

# re: Inclusion of JavaScript Files

I haven't had to update since I started using the Google CDN, but in the past I used "Replace in Files" to update it across a solution. I actually prefer doing it manually, since jQuery revs sometimes break plugins.

Adam Kahtava
July 07, 2008

# re: Inclusion of JavaScript Files

Multiple ContentPlaceHolders in a MasterPage coupled with the "One Library Path for All Scripts" approach have been working for me. A Placeholder in the HEAD for JavaScript and CSS, one in the body, and a Placeholder at the end of the HTML document - common Script references for all pages are included prior to the MasterPages Head ContentPlaceholder. Occasionally I'll revert to using the ClientScriptManager when developing reusable controls.

It's a messy approach, but it works when the entire team embraces it.

I like the Custom Script Manager approach. Are you using something similar for you CSS?

Rick Strahl
July 07, 2008

# re: Inclusion of JavaScript Files

@Dave - for a single tool versioned works, but I think it gets messy if you have to update several tools at once. Most plugins aren't versioned anyway so I suppose it's not a big issue. For me the big thing is getting controls that use scripts and page embedded scripts organized.

@Adam - currently I'm just experimenting with the custom script manager approach. I have it in one application and it works actually pretty well. Basically it works with a single instance of the control on the page with a factory for code that accesses that single instance or creates a new control if one doesn't exist on the page. The idea is that there's always just one instance that's globally accessible. This way both markup and code use the same manager and you can tell whether there's overlap in loaded resources (to some degree).

I suppose the same could be applied to CSS - possibly in the same control (it would actually work since the children are HTML controls even with CSS IntelliSense intact).

Bertrand Le Roy
July 07, 2008

# re: Inclusion of JavaScript Files

Hey Rick. We're very aware that always including Microsoft Ajax is a big problem for people like you who wish to use other libraries and that ScriptManager is useful way beyond Microsoft Ajax. We have an active bug about that. We're also looking at script registration in MVC, and you may be able to use some of the extension methods we'll have in there with regular WebForms. I'm not sure how that will play with IntelliSense in the short term though.

Dan Finch
July 07, 2008

# re: Inclusion of JavaScript Files

Before I moved to MVC I had a great control which I could place anywhere (aspx, master, ascx) in markup to reference a script needed and towards the end of the request (I think step 37207 of the page lifecycle) I'd do some minifying/combining/caching tricks along the lines of Yahoo's recommendations.

Rick Strahl
July 07, 2008

# re: Inclusion of JavaScript Files

@Bertrand - yes we had talked about this before. Is this by any chance planned to get fixed in SP1?

Matt Kellogg
July 07, 2008

# re: Inclusion of JavaScript Files

Well, not sure if this is optimal, but it works for my current situation.

I'm using ASP.NET MVC with the nhaml view engine on my current project. In my Application.haml layout (a master page to those using the web forms view engine) I put a quick " = ViewData["Javascript"] " which outputs all of the scripts for that page (the same is done for css).

This viewdata object is populated via an action filter sitting on my application's base controller class. This action filter basically reads several locations /Views/Shared/<environment>.js.assets, /Views/<Controller>/<environment>.js.assets, or /Views/<Controller>/<ActionName>.<environment>.js.assets and caches the output and compiles them.

The dev/production problem is solved by a (yet to be written!) ms build task which compresses, and generates the appropriate prod.js.assets files.

The inspiration comes from ruby's asset packager. I couldn't come up with a way to do what it does exactly, but this is pretty close and solves the problem pretty well in my opinion.

If someone wants to hit me up on MSN or via email (matthew dot kellogg at gmail dot com) and talk about this I'd be more than happy to share the code and talk about it.

Bertrand Le Roy
July 07, 2008

# re: Inclusion of JavaScript Files

Not SP1, no. There *is* a dirty trick that you might want to try. I know it's ugly so don't scream yet. The idea is the following:
<ScriptReference name="MicrosoftAjax.js" path="~/Script/jquery.js"/>

This should replace MicrosoftAjax with jQuery. Pretty much.
Also, I wanted to correct you on one point: MicrosoftAjax.js is really less than 22kB gzipped and in release mode. I'm not sure where you got that 65kB figure...

Bertrand Le Roy
July 07, 2008

# re: Inclusion of JavaScript Files

Nevermind. That doesn't quite work. Almost but not quite.

Bertrand Le Roy
July 07, 2008

# re: Inclusion of JavaScript Files

Woohoo! Actually, I managed to apply some magic and make it work:
http://weblogs.asp.net/bleroy/archive/2008/07/07/using-scriptmanager-with-other-frameworks.aspx

Igor Loginov
July 07, 2008

# re: Inclusion of JavaScript Files

A couple of general thoughts on the topic (maybe useless, who knows)...

Actually, ASP.NET is a good HTML/XHTML generator with event handling and therefore with pretty complex lifecycle. Generation is done on several levels: master page - page - control, so it is recursive. We need a way to describe included resourses in declarative syntax and/or in code behind. Let's define a rule: each entity in hierarchy "master page - page - control" embeds its specific resources but only "claims" the need in common resources. Thus, if control (and derived page and master page) has ResourceDependencies property-collection, we will know what include lines must be added at web page generation. I see several issues here:
1) it should be a collection, so, we need delimited string with no spaces in names for daclarative part,
2) we spoke about names of includes but not about their paths,
3) places of includes in markup is undefined,
4) there is still no versioning.
Can we resove the above problems? #1 is not a real problem. #2 - as for me it's even bon tone to keep all css and all javascripts in their pre-defined directories. #3 - more complex, but actually, there are two good places for resources: in the head, and at the end before closing html. #4 is the most complex problem: we can add version attribute for embedded resource, but creating manifests for css/javascript directories looks weird.

And the final hidden issue: seems, only Microsoft guys can do this :)

Does anybody want to share his opinion on this?

Igor Loginov
July 08, 2008

# re: Inclusion of JavaScript Files

Morning add-on:
Forget about my idea. To implement it we will need "repository" (HashTable or HashSet) for the whole application, so, ResourceDependencies contain only keys... But I see that this way is outside the mainstream of development.

Michael James
July 09, 2008

# re: Inclusion of JavaScript Files

I actually had thought about this a while back as I was doing a lot of projects that I was making Web Controls for and I wasn't sure if they had the appropriate libraries or files loaded.

It's a tough issue to solve and I don't think theres one definate right answer. Anyway as part of this I wrote my own JSloader. This would ensure the page loads a JSLibrary or file only once per page and it could be used multiple times. http://blog.mjjames.co.uk/2008/05/javascript-library-and-file-loader.html

Not sure what people think about it? It does need more work doing but I think its a good firm base. If people are interested in it, I'll get it on codeplex and then maybe open it up for contributions.

Peter
July 09, 2008

# re: Inclusion of JavaScript Files

"[...]IIS handle the caching (which it does very well) and the compression (which it does not so well)."

Why do you say it doesn't handle compression well?

Rick Strahl
July 10, 2008

# re: Inclusion of JavaScript Files

IIS 6 has a number of issues with files not getting cached for some non-definable reasons. In addition configuring IIS 6 is a pain in the ass. IIS 7 is better, but it too has some odd rules about how things start getting compressed.

Richard Deeming
July 10, 2008

# re: Inclusion of JavaScript Files

I think the biggest problem with the ScriptManager control (and the ClientScriptManager class that it relies on) is that they are completely useless without a server form on the page. You can add a ScriptManager to a form-less page if you set the EnablePartialRendering property to false, but none of the scripts you register will be output to the page.

Since MVC views can't use server forms, I'm hoping that the ASP.NET team has a fix up their collective sleeve! Even without MVC, it would be nice to have a standard way to register scripts on a page that doesn't have a server form.

Rick Strahl
July 10, 2008

# re: Inclusion of JavaScript Files

@Richard - that's a very good point. Actually I haven't checked that in my own control and it's something to make sure that it works. OTOH, I am pretty sure that these controls won't work anyway since MVC doesn't have a ClientScript Manager or anything else to control script injection into the page.

Which IMHO is a glaring problem of MVC in the first place - no control over a 'page' and insertion of anything into it other than what the View can directly render. Pretty much eliminates any chance of building complex controls for MVC. Low level and page level construction is all fine and good, but if you have no control (or even a 'convention') for consistent page injection you're forever stuck reinventing the wheel from scratch.

GH
July 17, 2008

# re: Inclusion of JavaScript Files

Rick, your statement on the unavailability of the ScriptManager until 3.5 is incorrect.
It's available as part of the AJAX Extensions as part of ASP.Net 2.0; I'm using it to add my script.

Another advantage of using the ScriptManager is that it allows you to embed your JavaScript files into your assembly, and then roll all of them up into one file using the ScriptCombine attribute in the AssemblyInfo class; so instead of multiple round trips to the server, you just end up with one, AND the ScriptManager handles the client-side cache expiry headers etc. appropriately without the need for us to set them.

Agree with you though on the inability to stop it loading all the other rubbish - I'd like to be able to pick and choose the scripts it brings down rather than an enormous download I'm not going to use. Indeed, I often get errors in my app because my scripts can't load fast. And why? Because the page is still loading the Microsoft rubbish!! It's a difficult choice for sure.

Jax
June 11, 2009

# re: Inclusion of JavaScript Files

I know some guy mentions that he alters his registry so that in Visual Studio under his web form page Default.aspx he has his codebehind Default.aspx.vb AND Default.aspx.js:

He runs this:
Visual Studio 2005

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Projects\{E24C65DC-7377-472b-9ABA-BC803B73C61A}\RelatedFiles\.aspx\.js]
@=""

Visual Studio 2008

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Projects\{E24C65DC-7377-472b-9ABA-BC803B73C61A}\RelatedFiles\.aspx\.js]
@=""

Does anyone see any downside to this?

Tim
March 06, 2011

# re: Inclusion of JavaScript Files


Hi I know this is an old post but I can't help but throw my solution out there because I think is solves a lot of the issues you're dealing with.
I have a header control that loads dynamically in the header. All of the script paths (development and production) are stored in the database. The control returns the appropriate javascript paths for the application and page passed into it. Some of the scripts are loaded on every page of the app others are only loaded on the specific page passed into the sproc. I also pass in the database key DBLIVE or DBDEV since both scripts are stored in the database I can pick the scripts, live or dev that I want. When I want to upgrade I simply logically delete the old version and add the new version. I do the same with css files.

I also have a footer control to load scripts that have to be at the end of the document like google analytics.

This method has saved me a lot of trouble

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024