JavaScript Query String

kick it on DotNetKicks.com

Query Strings

When I started coding more client-side JavaScript in replace of ASP.NET code, I was surprised to find there there's no object in JavaScript that breaks down a query string into a key (or name)/value collection, as this.Request.QueryString does in ASP.NET.

Here's a URL with a query string:

http://prettycode.com/search?query=code&pageSize=20

The query string is everything that including and after the ? delimiter: ?query=code&pageSize=20. It's a simple collection of names and values, each seperated by &. The name comes first, then =, then the value. Name/value pairs are typically referred to as query string parameters.

As a Collection In JavaScript?

One would think that as prevalent as the use of query strings are for passing parameters to web servers, there would be an object in JavaScript that served as a collection of these pairs hung off of window.location, where the URL-related properties lives. But there isn't.

The property window.location.search will return the query string portion of the URL (including the ?), but there's no member of window.location that breaks search into a name/value collection. Say you're at the URL http://prettycode.org/search?query=code&pageSize=20:

// same as: alert("?query=code&pageSize=20");
alert(window.location.search);

If there’s no query string (e.g. at the URL http://prettycode.org), search is an zero-length string:

// same as: alert("");
alert(window.location.search);

Introducing window.location.querystring

In lieu of such a name/value object, I’ve written a very simple JavaScript that represents the query string as a dictionary object. The object’s members are the query string names, and the member values are the name values. Make your life easier and drop the window.location.querystring script somewhere in your global scope. Using the script is as simple as:

var querystring = window.location.querystring;
 
var myValue = querystring["myKey"];
 
for (var key in querystring) {
    var value = querystring[key];
    alert(key + "=" + value);  
}

Here it is, with no dependencies:

window.location.querystring = (function() {
 
    // by Chris O'Brien, prettycode.org
 
    var collection = {};
 
    // Gets the query string, starts with '?'
 
    var querystring = window.location.search;
 
    // Empty if no query string
 
    if (!querystring) {
        return { toString: function() { return ""; } };
    }
 
    // Decode query string and remove '?'
 
    querystring = decodeURI(querystring.substring(1));
 
   // Load the key/values of the return collection
 
    var pairs = querystring.split("&");
 
    for (var i = 0; i < pairs.length; i++) {
 
        // Empty pair (e.g. ?key=val&&key2=val2)
 
        if (!pairs[i]) {
            continue;
        }
 
        // Don't use split("=") in case value has "=" in it
 
        var seperatorPosition = pairs[i].indexOf("=");
 
        if (seperatorPosition == -1) {
            collection[pairs[i]] = "";
        }
        else {
            collection[pairs[i].substring(0, seperatorPosition)] 
                = pairs[i].substr(seperatorPosition + 1);
        }
    }
 
    // toString() returns the key/value pairs concatenated
 
    collection.toString = function() {
        return "?" + querystring;
    };
 
    return collection;
})();

Edge Cases

Ah yes, those annoying what-ifs. How are malformed or missing query strings or parameter values handled? Take a look.

Pair Doesn't Exist

If you're expecting a key/value pair, but it isn't present, undefined is returned:

// http://prettycode.org/?pageSize=20
 
if (!window.location.querystring["q"]) // == undefined
    alert("You forgot to enter a query");

No Query String

If there is no query string, window.location.querystring.toString() returns an empty string, like Request.QueryString.ToSring() does in ASP.NET:

// http://prettycode.org
 
if (!window.location.querystring.toString()) // == ""
    alert("No query string");

No Name/Value Pairs

If there are no name/value pairs, window.location.querystring's only member is toString(), and it will return the value of window.location.search, decoded:

// http://prettycode.org/?
var s = window.location.querystring.toString(); // == "?"

Missing Parameter Value

If a query string parameter is specified but has no value, the value is an empty string, also ASP.NET style:

// http://prettycode.org/?query=&pageSize=20
 
if (!window.location.querystring["query"]) // == ""
    alert("No query specified");

Incomplete Pair

If there's at least one pair and additional data in the query string that isn't a pair, it's added with an empty value:

// http://prettycode.org/?query=seattle&pageSize	
 
var querystring = window.location.querystring;
 
var query    = querystring["query"];    // == "seattle"
var pageSize = querystring["pageSize"]; // == ""
 
if (!pageSize) {
    pageSize = 20;
}

Multiple Pairs with the same Name

Lastly, if the parameter name is specified twice, its value will the be the last defined:

// http://prettycode.org/?query=seattle&query=portland	
 
var query = window.location.querystring["query"]; // == "portland"

kick it on DotNetKicks.com

Tags: , , ,

7 Responses to “JavaScript Query String”

  1. Garry Shutler Says:
    April 22nd, 2009 at 7:00 am

    Why no make toString just return window.location.search? Saves encoding what you just decoded.

  2. WebDevVote.com Says:
    April 22nd, 2009 at 9:39 am

    You are voted!
    Track back from http://webdevvote.com

  3. Miron Says:
    April 22nd, 2009 at 12:57 pm

    You can use this method:

    //
    // Get query string parameter
    //
    function GetQuerystringParam(name, url) {
        name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
        var regexS = "[\\?&amp;]" + name + "=([^&amp;#]*)";
        var regex = new RegExp(regexS);
        if (typeof (url) == 'undefined') url = window.location.href;
        var results = regex.exec(url);
        if (results == null)
            return "";
        else
            return results[1];
    }
  4. Chris Says:
    April 22nd, 2009 at 8:34 pm

    @Miron: While that works, every time GetQuerystringParam() is invoked, the URL has mutiple regular expressions matched against it, which is bad practice. The URL should be broken down into a collection or examined for a name/value pair once; not every time a query string parameter value is needed.

  5. Chris Says:
    April 22nd, 2009 at 8:38 pm

    @Garry: Good point. In response, I’ve changed the script to avoid manually rebuilding the decoded query string toString() returns, and instead, the decoded location.search value is returned by toString(). Thanks again!

  6. Miron Says:
    April 24th, 2009 at 2:54 am

    In other hand, why should you parse all the query string parameters when you need only one?
    Also, you are talking about client side code and regular expression over not so long string… The performance impact is negligible.

  7. Chris Says:
    April 24th, 2009 at 10:54 am

    @Miron: I don’t think the GetQuerystringParam() you’ve shown is a bad tool. My concern with using it is that it’s easily abused because it’s a function. What if another developer is maintaining the app. and wants to grab another value in the query string? They’ll likely just call the same function you used. The consequence isn’t performance (as you say, it’s negligible). Code should be written with reuse in mind, and defensively against misuse—as a practice. A function encourages repetitious, procedural, imperative design, while an object encourages encapsulation, separation of concerns, and adherence to DRY. The consequence (at least in my mind) is that such a function has greater potential to encourage sloppy code.

    I think what is probably a great idea is replacing the name/value parsing logic in my code with the regular expressions you’re using if the regex is bulletproof. :)

Leave a Reply

To include code, use <pre lang="csharp">// code...</pre>, where "csharp" is any language (e.g. "javascript", "html", "xml", etc).