Introduction
This article describes a way for central control of jQuery version in an application which will make code consistent and make it easy to upgrade jQuery.
Background
I have been worked with a web application which has more than 10 years history. It has no centralized jQuery version control, and there are several different versions of the jQuery library co-existing in it, from 1.3.2 to 1.8.3. Each developer arbitrarily or randomly (from copy paste) decides a specific version jQuery he wants to use. Indeed sometimes jQuery is referenced in some pages whereas it has never been used at all (again, from careless copy paste). This brings many headaches, the same code may not be used in some other pages due to version differences, and if I want to upgrade jQuery I need to modify about 40 pages one by one. To get rid of this problem and make code easier to maintain, I came up with this solution.
Solution description
When I was thinking about this solution, I knew I needed to achieve two points:
- Centralize the jQuery version control;
- Allow the developer to freely choose any specific version jQuery as he wants (because of the reality that some jQuery plug-in has dependency on some specific older version jQuery).
It is not good to put a reference in the master page because:
- there are multiple master pages in the application
- many pages are not using jQuery so there is no reason for these pages to be loaded with jQuery by default.
The general idea is to override the Render event to force to inject a jQuery reference into the first position of the head segment
Step 1: Add a new key into web.config, which is the central place to store the standard jQuery version.
<add key="StandardJQueryVersion" value="1.10.1" / >
Step 2: Add an enum type to list all the jQuery reference modes. Here Standard
means the current common jQuery version in the system, which is the newly added web.config key from step 1.
public enum JQueryReferenceMode
{
None = 1,
Standard,
Customized
}
Step 3: As usual, the application has a base page class which is inherited by almost all of the web pages in the system. I add three class level variables to indicate the jQuery reference rendering mode.
protected JQueryReferenceMode mJQueryReferenceMode =
JQueryReferenceMode.None;
protected bool mIncludeMinifiedJQuery = true;
protected string mCustomizedJQueryVersion =
WebConfigurationManager.AppSettings["StandardJQueryVersion"];
Step 4: Override the Render
event of the base page:
protected override void Render(HtmlTextWriter writer)
{
if (mJQueryReferenceMode != JQueryReferenceMode.None)
{
RenderWithJQueryReference(writer);
}
else
{
base.Render(writer);
}
}
Add the method for rendering page with jQuery reference, standard or customized.
protected void RenderWithJQueryReference(HtmlTextWriter writer)
{
System.IO.MemoryStream myMemoryStream = new System.IO.MemoryStream();
System.IO.StreamWriter myStreamWriter = new System.IO.StreamWriter(myMemoryStream);
System.Web.UI.HtmlTextWriter myHtmlTextWriter = new HtmlTextWriter(myStreamWriter);
base.Render(myHtmlTextWriter);
myHtmlTextWriter.Flush();
myHtmlTextWriter.Dispose();
System.IO.StreamReader myStreamReader = new System.IO.StreamReader(myMemoryStream);
myStreamReader.BaseStream.Position = 0;
string pageContent = myStreamReader.ReadToEnd();
myStreamReader.Dispose();
myMemoryStream.Dispose();
if (pageContent.Trim().EndsWith("</html>"))
{
int headStartTagBeginIndex = pageContent.IndexOf("<head", 0);
int headStartTagEndIndex = pageContent.IndexOf(">", headStartTagBeginIndex);
string pageContentPart1 = pageContent.Substring(0, headStartTagEndIndex + 1);
string pageContentPart2 = pageContent.Substring(headStartTagEndIndex + 1);
string jQueryVersionNumber = mJQueryReferenceMode == JQueryReferenceMode.Standard ?
WebConfigurationManager.AppSettings["StandardJQueryVersion"] : mCustomizedJQueryVersion;
jQueryVersionNumber += mIncludeMinifiedJQuery ? ".min" : "";
string jQueryPath = "../script/JQuery/jquery-" + jQueryVersionNumber + ".js";
string jQueryReference = "\r\n <script src=\"" + jQueryPath +
"\" type=\"text/javascript\"></script> \r\n ";
pageContent = pageContentPart1 + jQueryReference + pageContentPart2;
}
writer.Write(pageContent);
}
Step 5: To reference the jQuery version you need, you only need to set the variables in the page_load
event.
- Standard jQuery reference
protected void Page_Load(object sender, EventArgs e)
{
mJQueryReferenceMode = JQueryReferenceMode.Standard;
- Customized jQuery reference
protected void Page_Load(object sender, EventArgs e)
{
mJQueryReferenceMode = JQueryReferenceMode.Customized;
mCustomizedJQueryVersion = "1.8.3";
Benefit
With this solution it becomes quite easy to control the jQuery version. If you want to upgrade your jQuery version, you only need to change one key value in web.config. It is easy to roll back if you are not satisfied with the upgraded version of jQuery, just change it back. The developer no more needs to care about which version jQuery he is using, unless he wants to define the customized jQuery version in his page.
Some questions
Question 1: Why not use some more elegant code-behind to add a Literal
control into the head
's Controls
collection, like this?
LiteralControl jQueryReferenceControl = new LiteralControl(
"<script src=\"..script/JQuery/jquery.1.10.1.js\" type=\"text/javascript\"></script>");
Page.Header.Controls.Add(jQueryReferenceControl);
Answer: This code will break if the web page has a script block which contains a server tag, for example:
var nameTextBoxId = '<%= NameTextBox.ClientID %>';
Question 2: Why should the script be in the first position inside the head segment?
Answer: This guarantees that jQuery is always referenced before any script block that may contain jQuery code. I got some problems because of the JavaScript reference order.
Question 3: Why do we need this condition "if (pageContent.Trim().EndsWith("</html>"))
"?
Answer: Partial update will not render full page HTML. In that case, there will be no head segment in the content.