Simplifying prevention of undesired Css/Js file caching

September 26, 2008 at 8:12 PMBen

It's an unfortunate reality that after updating an external CSS or JavaScript file referenced from a web page, not all browsers that have been to your site before will detect an updated file it has previously cached.  When this happens, browsers are running outdated JS and applying old styles to your page elements.

One of the common workarounds for this situation that I've found quite effective is to append some value to the query string of the external file.  So instead of the typical link (to a CSS file) in the Head section of your document,

<link href="styles.css" type="text/css" rel="stylesheet" /> 


You instead use a link with an arbitrary value following the actual file name:

<link href="styles.css?v=1" type="text/css" rel="stylesheet" />


Browsers cache the CSS file with an Id of "styles.css?v=1".  If you update your CSS file, you can change the "v=1" to "v=2" in your HTML page and the browser treats styles.css?v=2 as a different file than styles.css?v=1.  Since styles.css?v=2 isn't already cached, the browser will fetch the latest copy of styles.css from your web server.  Constantly modifying (and trying to remember to modify) the query string value when I make changes to CSS and JS files has always been a manual task for me.  Recently, however, I created a mechanism to automate this process.

The automated process appends the timestamp of the external file (CSS of JS) to the query string following the file's name that is sent to the browser.  The file timestamp is stored in the .NET cache with a cache dependency to the actual file on the web server.  Whenever I update the CSS or JS file, the timestamp is automatically removed from the cache and the next time a visitor arrives at the page and the timestamp is needed, the timestamp is retrieved and stored in cache.  The purpose of the cache is to obviously reduce the amount of file I/O and overall time required in getting the page to the browser.  I've fallen in love with this process as it's made my life easier!

The download link at the bottom of this post contains the code for a static GetFileWithVersion() function.  The same function can be used for any external file that you want to prevent browsers from mistakenly hanging onto a copy of after you may have updated the file.  At present, GetFileWithVersion returns a string value containing the filename and appended query string value.  It's the caller's responsibility to add the necessary link or script tag to the head section of the page.

Example usage of the GetFileWithVersion() function with a CSS file:

string CssFileWithVersion = utils.GetFileWithVersion("MainCss", "~/styles.css");
 
System.Web.UI.HtmlControls.HtmlLink cssLink = new System.Web.UI.HtmlControls.HtmlLink();
cssLink.Href = CssFileWithVersion;
cssLink.Attributes.Add("type", "text/css");
cssLink.Attributes.Add("rel", "stylesheet");
this.Header.Controls.Add(cssLink);


Example usage of the GetFileWithVersion() function with a JavaScript file:

string JsKey = "MainJs";
if (!ClientScript.IsClientScriptIncludeRegistered(this.GetType(), JsKey))
{
    string JsFileWithVersion = utils.GetFileWithVersion(JsKey, "~/tools.js");
    ClientScript.RegisterClientScriptInclude(this.GetType(), JsKey, ResolveClientUrl(JsFileWithVersion));
}


I've been using the GetFileWithVersion() function from master pages and standalone pages for a few weeks now.  Putting this type of mechanism in place is definitely a time saver and since implementing this, I personally haven't seen or heard of any issues with old CSS or JavaScript showing up.

Code Download (1.74 kb)

Posted in: Development

Tags: ,

W3C Validated Links

September 21, 2008 at 9:33 PMBen

Every so often I'm at a website and notice a "This page is Valid XHTML" link somewhere on the page.  The link takes you to the W3C's validator page, examines the HTML of the page you came from and gives a report.  If the HTML validates without any issues, you get a nice green bar with a message stating something similar to:

This document was successfully checked as XHTML 1.0 Transitional!

The problem is that of the few times I've bothered clicking on one of these links on someone's site, not uncommonly the validator comes back reporting errors:

Errors found while checking this document as XHTML 1.0 Transitional!

... oops, that's not supposed to happen!  In fairness, it's usually just a couple of minor issues that don't cause any major issues on the website.  But, the red text and the "Errors found" message gives a bad impression.  I would imagine when the site was originally put together, the HTML validated and the designer slapped a "Validated" link on the page.  Most likely the HTML that doesn't conform to the spec crept into the page during subsequent modifications and updates.

Unless you're selling or showcasing website templates, putting a "Page Validated" link on your website doesn't seem worth it.  These links are meaningless to almost all visitors and they require the site maintainer to constantly revalidate their HTML when making modifications.  When the site maintainer forgets to re-validate or doesn't know he should re-validate, you end up giving a worst impression to the visitor who clicks on your "Validated" link and finds errors than if you never had the link in the first place.

Posted in: Opinion

Tags: ,

JavaScript Events Not Firing

September 21, 2008 at 3:45 PMBen

It's important to realize a JavaScript event is not guaranteed to fire.  The events I have in mind are events such as onmouseout and onmouseover.  I'm sure there are many other events that also may not fire depending on the circumstances at the time the events would normally fire.  If the element is right up against the edge of the browser viewport when the mouse moves away from the element and out of the browser, or if the browser or computer is busy processing something else at the time the event would be expected to occur, it may just not happen.

While working with jQuery recently I created a rollover image with the hover() function to show a different image when the mouse rolls over the image.  The particular piece of code was:

oImgBtn.hover(
  function(){ this.src = this.src.replace('.gif','_over.gif'); },
  function(){ this.src = this.src.replace('_over',''); }
);


This code works good assuming your rollover image has the same filename as the original image with "_over" at the end of the filename.  The problem occurred one time while testing the page.  I noticed the image replacement was not occurring and I was getting some ugly flickering when hovering over the image.  Fortunately, I had Firebug already turned on and checking the Net tab showed a red item indicating a 404 error.  The file that the browser was getting a 404 on had this filename:

myImage_over_over.gif

Apparently, one of the previous times I had rolled over the image, for whatever reason the onmouseout event didn't fire leaving the images source as myImage_over.gif (the rollover image).  The next time I rolled over the image, the onmouseover event fired changing the image's source from myImage_over.gif to myImage_over_over.gif.  Not good!  A very easy and effective solution in this case was to make sure a preexisting "_over" in the image source was removed prior to adding it back in the onmouseover event handler.

oImgBtn.hover(
  function(){ this.src = this.src.replace('_over', '').replace('.gif','_over.gif'); },
  function(){ this.src = this.src.replace('_over',''); }
);


This is a much more robust method of making sure things don't fall apart in the rare event a JavaScript event doesn't fire.  Although rollover images are not usually critical to a page's functionality, it's still a bad experience to the end user if they see things flickering that shouldn't be flickering.  This also makes you realize that for any client side code, it's important to not assume events will always fire and be sure your code is defensively designed so it can recover from unexpected situations.

Posted in: Development

Tags: ,

It's Alive!

September 20, 2008 at 9:28 PMBen

This is the first blog post on Ben's Quarters.  If you search for a post prior to this one, you simply won't find one!

I'm excited to finally put a blog on the web.  I can't tell you how many times I've had some code sample, problem, idea, rant or thought to share, but didn't have anywhere to post!  Well, those days are now over.  Although I don't anticipate very frequent posting, I do hope to post on a consistent basis.

If you don't know me, don't fret -- I put together an "About Me" page (see link on left side).

The focus of posts on Ben's Quarters will be programming and software development related.  This is a large field.  My software development career has revolved around Microsoft technologies -- .NET, Visual Studio, SQL Server, etc.

I finally got a chance to work with jQuery a couple of weeks back.  I'm honestly not crazy about these types of libraries and have actually never used a JS library prior to jQuery.  There was definitely a learning curve and I wouldn't say that I can't live without it.  It seems like most JavaScript I need is not a big deal to just create straight without any libraries.  I see the biggest benefit of using a library such as jQuery is the cross browser support it gives you.  But again, the JavaScript I usually create without jQuery for validating input and manipulating some elements on the page already seems to run fine in the browers I test against.  I still don't mind incorporating jQuery into some web projects I work on, but don't feel like I need to.

I actually have a short jQuery related post I'll get up shortly.  It'll be post #2 Wink

Posted in: Development | General

Tags: ,