Wednesday, February 10, 2010

HTML Submit button still submits even when JavaScript returns false

Here's something that's caught me out a couple of times in the past so I'm documenting it to remind myself.

My web page has a submit <input> tag that looks like this:

<input type="submit" value="Create" />

I want to do some client side validation before I POST the page back to the server so I modify it to call my client side JavaScript function by adding the onclick attribute:

<input type="submit" value="Create" onclick="SubmitCreate();" />

The JavaScript does a bit of validation:

function SubmitCreate() {
    var selectedNodes = tree.getSelectedNodes();
    if (selectedNodes.length < 1) {
        alert('Please select at least one node.');
        return false;
    }
}

The problem is that even though the SubmitCreate() function is returning false this is not preventing the page from being submitted.

The reason, when shown, is obvious. The onclick attribute should return the JavaScript and not just call it.

<input type="submit" value="Create" onclick="return SubmitCreate();" />


 

Friday, February 5, 2010

Viewing connections to a server

I keep on forgetting how to do this so here it is documented for my convenience.

Start Performance Monitor (PerfMon)

Click + to add new monitor.

Enter computer's name

To view connections for a web service

From Performance object drop down select "Web Service"

From Select counters from list drop down select "Current Connections"

From Select instances from list select "_Total"

To view connections for a web app

From Performance object drop down select "ASP.NET Apps v2.0.50727"

Tuesday, February 2, 2010

Combine, compress, and update your CSS file in ASP.NET MVC

After doing some analysis using Google's web master tools I discovered that I needed to make some improvements to how the CSS file(s) on a busy site were being delivered. This post follows on from and improves on Automatically keeping CSS file current on any web page.

  • The CSS files need to be combined into a single file.
  • It needs to be compressed.
  • It needs to be cached, but only until it's changed.
  • Image references need to be dynamic.

I'm using ASP.NET MVC so the first thing that I did was to add an Action to a Controller that was to be the new CSS file. Instead of referencing a physical file on disk the CSS file has now become a resource that follows this pattern:

~/Site/Css/20100201105959.css

Site is the controller, Css is the action, and 20100201105959.css is the single parameter that this action accepts.

This is what the SiteController class looks like:

[CompressFilter]
public class SiteController : Controller
{
    static ContentResult cr = null;

    [CacheFilter(Duration=9999999)]
    public ActionResult CSS(string fileName)
    {
        try
        {
            if (cr == null)
            {
                StringBuilder sb = new StringBuilder();
                foreach (string cssFile in Constants.cssFiles)
                {
                    string file = Request.PhysicalApplicationPath + cssFile;
                    sb.Append(System.IO.File.ReadAllText(file));
                    sb.Append("!!!!!");
                }

                sb.Replace("[IMAGE_URL]", StaticData.Instance.ImageUrl);

                cr = new ContentResult();
                cr.Content = sb.ToString();
                cr.ContentType = "text/css";
            }

            return cr;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Trace.TraceError(ex.Message);
            return View();
        }
    }
}

Notice that we have a couple of filters on the class and method. The [CompressFilter] on the class will check the browser's capability for gzip or deflate and activate compression for any action/method in this class. The [CacheFilter] will add a 3 month "cache this file" direction to the HTTP header of the HTML that's returned.

The Constants.cssFiles returns a list of root-relative CSS files that need to be combined into the single response.

The Replace() function on the StringBuilder allows us to put a tag in the CSS files so that any references to images can be dynamically determined at runtime. This allows us to run the images off a url such as http://localhost:1234/images/ during development and http://images.mysite.com/ in production. Storing images in a subdomain instead of the main domain will improve performance because more images will be able to be downloaded in parallel by the browser.

The single fileName parameter is a dummy parameter that is needed to make the URL different when one of the underlying CSS files is modified but still allow optimum caching in the browsers. i.e. by changing this value in the HTML we return we force the browser to re-fetch the CSS but only when one of the underlying CSS files change.

The ContentResult is a static variable so that the building of this CSS file is a single hit after the App Pool has been recycled.

The following snippet of code is placed in the code behind page for the Site.Master. Normally you would not have a code behind page in the MVC model but I have not been able to work out how to dynamically inject the CSS file name into the master file any other way.

protected void Page_Load(object sender, EventArgs e)
{
    HtmlLink css = new HtmlLink();
    css.Href = String.Format("/Site/CSS/{0}", StaticData.Instance.CssFileName);
    css.Attributes["rel"] = "stylesheet";
    css.Attributes["type"] = "text/css";
    css.Attributes["media"] = "all";
    Page.Header.Controls.Add(css);
}

The CSS file name is generated with the following snippet of code:

string _CssFileName = null;public string CssFileName
{
    get
    {
        if (String.IsNullOrEmpty(_CssFileName))
        {
            DateTime dt = new DateTime(2000,1,1);
            foreach (string cssFile in Constants.cssFiles)
            {
                string file = System.Web.HttpContext.Current.Server.MapPath(cssFile);
                FileInfo fi = new FileInfo(file);
                DateTime lastWriteTime = fi.LastWriteTime;
                if (lastWriteTime > dt)
                {
                    dt = lastWriteTime;
                }
            }
            _CssFileName = dt.ToString("yyyyMMddHHmmss") + ".css";
        }
        return _CssFileName;
    }
}

We iterate through each of the CSS files and extract the most recent modified date. Using this date, we generate the the CSS file name. That way a different file name will be injected into the HTML whenever one of the CSS files is modified and this will force the browser to reload the new CSS file keeping it always up-to-date.

It may seem like a lot of work for a CSS file at first glance but the benefits are enormous and the added flexibility will allow you to change it on a whim.

I haven't shown you the Constants.cssFiles but that's just an array of file names. I was also thinking of implementing this as a loop that found all the *.css files in a folder. That way if you added a new CSS file it would automatically be included without a code change. However, the disadvantage of this is that you cannot predetermine the order in which the CSS files are combined into the single file and the order is usually important.

If, however, you did want to use that all-css-in-one-folder approach you could adopt a naming convention such as 01_myfile.css, 02_myfile.css and then sort the file names before combining them.

Google to help kill IE6

I got an email from Google this morning which in part said:

"...over the course of 2010, we will be phasing out support for Microsoft Internet Explorer 6.0..."

This is great news. I have a couple of sites that I work on that just don't work in IE6 and it's the bane of my life. I really don't want to be wasting investing time in getting IE6 to work when I could be adding new features and improving performance. 

With Google behind this I am hoping that this will accelerate users upgrading from IE6.

Also related are the IE6 Update and the IE6 No More sites.