Using minified CSS and JavaScript files everywhere in SharePoint 2010


In my previous post I showed you how you can automate minifying JavaScript and CSS files in Visual Studio 2010. As I also mentioned, SharePoint has limited support for using minified files. But just because something is not available out of the box doesn’t mean it’s not possible. Find out how to leverage the extensibility capabilities of the SharePoint 2010 platform and support using minified CSS and JavaScript files everywhere.

Using minified files is a great and easy technique for optimizing your SharePoint solution for performance. Out-of-the-box SharePoint 2010 provides the ScriptLink control which allows you to automatically switch between raw and minified versions of your files. It has however two flaws. First of all it supports only JavaScript files and just as you can minify JavaScript files, you can also minify your CSS files. Another thing that you have to keep in mind, if you want to use the standard ScriptLink control with minified files, is that it works only for JavaScript files deployed to the LAYOUTS folder on the file system.

Using minified files everywhere

If you want to use minified CSS and JavaScript files everywhere you will need a solution other than the standard ScriptLink control. One solution that you could consider is creating a custom control that you would add to your Master Page and which would automatically switch between the raw and minified version of the asset file depending on the mode in which the Web Application is working. Such control could look as follows:

using System;
using System.IO;
using System.Web;
using System.Web.UI;
using Microsoft.SharePoint.Utilities;

namespace Mavention.SharePoint.Controls {
    public enum AssetType {
        CSS,
        JavaScript,
        Other
    }

    public class AssetLinkControl : Control {
        public string Href { get; set; }
        public bool WithDebug { get; set; }

        protected override void Render(HtmlTextWriter writer) {
            if (!String.IsNullOrEmpty(Href)) {
                AssetType assetType = AssetType.Other;
                string href = GetAssetUrl(Href, WithDebug, out assetType);

                switch (assetType) {
                    case AssetType.CSS:
                        RenderCssLink(href, writer);
                        break;
                    case AssetType.JavaScript:
                        RenderJsLink(href, writer);
                        break;
                }
            }
        }

        public static string GetAssetUrl(string url, bool withDebug, out AssetType assetType) {
            string assetUrl = url;
            assetType = AssetType.Other;

            if (!String.IsNullOrEmpty(assetUrl)) {
                assetUrl = SPUtility.GetServerRelativeUrlFromPrefixedUrl(assetUrl);
                assetType = GetAssetType(assetUrl);

                if (withDebug && HttpContext.Current.IsDebuggingEnabled) {
                    assetUrl = GetDebugUrl(assetUrl, assetType);
                }
            }

            return assetUrl;
        }

        private static AssetType GetAssetType(string url) {
            AssetType assetType = AssetType.Other;

            if (!String.IsNullOrEmpty(url)) {
                string extension = Path.GetExtension(url);
                switch (extension) {
                    case ".css":
                        assetType = AssetType.CSS;
                        break;
                    case ".js":
                        assetType = AssetType.JavaScript;
                        break;
                }
            }

            return assetType;
        }

        private static string GetDebugUrl(string url, AssetType assetType) {
            string debugUrl = url;

            if (!String.IsNullOrEmpty(debugUrl)) {
                string extension = Path.GetExtension(debugUrl);
                switch (assetType) {
                    case AssetType.CSS:
                        extension = ".css";
                        break;
                    case AssetType.JavaScript:
                        extension = ".js";
                        break;
                }

                debugUrl = String.Concat(debugUrl.Replace(extension, String.Format(".debug{0}", extension)));
            }

            return debugUrl;
        }

        private void RenderJsLink(string href, HtmlTextWriter writer) {
            writer.AddAttribute(HtmlTextWriterAttribute.Src, href);
            writer.RenderBeginTag(HtmlTextWriterTag.Script);
            writer.RenderEndTag();
        }

        private void RenderCssLink(string href, HtmlTextWriter writer) {
            writer.AddAttribute(HtmlTextWriterAttribute.Rel, "stylesheet");
            writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/css");
            writer.AddAttribute(HtmlTextWriterAttribute.Href, href);
            writer.RenderBeginTag(HtmlTextWriterTag.Link);
            writer.RenderEndTag();
        }
    }
}

How it works

We start with retrieving the URL of the asset (line 21). To provide flexibility when defining URLs we first translate the URL to a server-relative URL by parsing tokens if applicable (line 39). Next we get the type of the asset that we are working with (line 40). While we could create two separate controls: one for CSS files and one for JavaScript files, in this sample we use one control for both types. The last step is to check whether the asset link points to an asset that is provided in two versions (raw and minified) and if the Web Application is in debug mode (line 42).

Using the WithDebug property allows us to use the AssetLinkControl both for referencing files that have already been minified (like the jQuery library) as well as files that we have created ourselves and that are available both as raw and minified files.

If the Web Application is in debug mode we convert the asset link to point to the debug version of the asset (line 43). Finally we return the URL and render it depending on the asset type (lines 23-30).

Using the AssetLinkControl

The AssetLinkControl can be used the best for declaratively registering assets in the Master Page and Page Layouts. To register an asset, all you have to do is to include the following snippet:

<Mavention:AssetLinkControl Href="~SiteCollection/Style Library/mavention/css/mavention.css" WithDebug="true" runat="server" />

Depending on the mode in which your Web Application is running, this control will either point to the minified or the debug version of the CSS file.

What the AssetLinkControl is not

Although the AssetLinkControl provides you with great flexibility, it is by no means a replacement for dynamically registering JavaScript or CSS files. The standard SharePoint 2010 CssRegistration and ScriptLink controls provide much more flexibility allowing you to determine how and when your assets should be loaded. Whenever you need to register a CSS or a JavaScript file on runtime you should use those standard controls instead. The great thing about how the AssetLinkControl is built, is that you can use its GetAssetUrl method to build a URL to your asset and then pass it to the standard SharePoint controls. Such approach allows you to extend the standard functionality of the SharePoint platform with additional flexibility and optimization for performance.

Others found also helpful: