JavaScript Query String

kick it on DotNetKicks.com

In the process of converting almost all the C# in our ASP.NET webpage codebehinds over to client-side JavaScript, we lost a useful member of the ASP.NET Page: its Request.QueryString. Take a look at this URL:

http://xml.piggy.com/default.aspx?q=software&page=10

The query string is everything that appears after the ‘?’ delimiter. As one can plainly see, it’s a simple collection of name or key/value pairs, each separated by an ‘&’. The key comes first, then a ‘=’, then the value. One would think that as prevalent as the use of query strings is for passing parameters to web servers to change the returned HTML document, 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.

The property window.location.search will return the query string portion of the URL, including the ‘?’ delimiter. For example, it would return “?q=software&page=10″ in the example URL at piggy.com above (if there’s no query string, search is an zero-length string). The problem with search is that the key/value pairs have to be manually parsed out. A collection-like object accessible in the same manor as a hash table (e.g. var value = collection[key]) is a much more convenient and useful way of representing the query string.

In lieu of such an object in window, I’ve created a very simple JavaScript snippet that represents the query string as an object. The object’s properties are the query string keys, and the properties’ values are the key values. Make your life easier and drop this puppy somewhere in your global scope. A closure is used to make it play nice with the neighbors.

location.querystring = (function() {

    // The return is a collection of key/value pairs

    var queryStringDictionary = {};

    // Gets the query string, starts with '?'

    var querystring = decodeURI(location.search);

    // document.location.search is empty if no query string

    if (!querystring) {
        return {};
    }

    // Remove the '?' via substring(1)

    querystring = querystring.substring(1);

    // '&' seperates key/value pairs

    var pairs = querystring.split("&");

    // Load the key/values of the return collection

    for (var i = 0; i < pairs.length; i++) {
        var keyValuePair = pairs[i].split("=");
        queryStringDictionary[keyValuePair[0]] 
                = keyValuePair[1];
    }

    // toString() returns the key/value pairs concatenated

    queryStringDictionary.toString = function() {

        if (queryStringDictionary.length == 0) {
            return "";
        }

        var toString = "?";

        for (var key in queryStringDictionary) {
            toString += key + "=" + 
                queryStringDictionary[key];
        }

        return toString;
    };

    // Return the key/value dictionary

    return queryStringDictionary;
})();

Getting the value for the “q” key in the piggy.com URL now is as simple as:

// If "q" isn't there, val is undefined
var val = location.querystring["q"];

And the collection can easily be enumerated too:

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

Lastly, the entire query string can be retrieved (includes the leading ‘?’) via toString():

        alert(window.location.querystring.toString());
About these ads

11 Comments »

  1. James said

    Thanks very much for this. I’ve modified your location.querystring function to handle querystrings containing array values, like you might have to deal with when using checkboxes in HTML forms. The problem is if you have a querystring like "?foo[]=1&foo[]=2" then Javascript treats the key as a string rather than an array assignment, and you end up with location.querystring["foo[]"]=2 rather than location.querystring["foo"]=[1,2].


    location.querystring = (function() {
        var result = {};
        var querystring = decodeURIComponent(location.search);
        if (!querystring) return result;
        var pairs = querystring.substring(1).split("&");
        var splitPair;
        for (var i = 0; i -1){
                //pair is an array assignment
                var key=splitPair[0].substr(0,splitPair[0].length-2);
                
                if(!result[key]){
                    //initialize the array
                    result[key]=new Array();
                }
                result[key].push(splitPair[1]);
            
            }else{
                //pair is a string assignment
                result[splitPair[0]] = splitPair[1];
            }    
        }
        return result;
    })();

    I can’t see any problems with this – it works for my purposes at any rate.

    Cheers

    James

  2. James said

    Sorry, forgot to escape my entities in the above post! The function should read:


    location.querystring = (function() {
        var result = {};
        var querystring = decodeURIComponent(location.search);
        if (!querystring) return result;
        var pairs = querystring.substring(1).split("&");
        var splitPair;
        for (var i = 0; i > pairs.length; i++) {
            
            splitPair = pairs[i].split("=");
            if (splitPair[0].indexOf("[]")<-1){
                //pair is an array assignment
                var key=splitPair[0].substr(0,splitPair[0].length-2);
                
                if(!result[key]){
                    //initialize the array
                    result[key]=new Array();
                }
                result[key].push(splitPair[1]);
            
            }else{
                //pair is a string assignment
                result[splitPair[0]] = splitPair[1];
            }    
        }
        return result;
    })();

    • Groovetrain said

      Been almost a year since this comment… but anyway, your >’s and <'s are reversed. Should be:

      for (var i = 0; i -1){
      //pair is an array assignment
      var key=splitPair[0].substr(0,splitPair[0].length-2);

      if(!result[key]){
      //initialize the array
      result[key]=new Array();
      }
      result[key].push(splitPair[1]);

      }else{
      //pair is a string assignment
      result[splitPair[0]] = splitPair[1];
      }
      }

      This now works for me, thanks!

  3. very useful. how would you remove a key/value from the query? Say, I want to strip out some and not others…

  4. Chris said

    The easiest way, Donal, would be to simply set the query string parameter value to an empty string:

    window.location.querystring[“key”] = “”;

    Most of the time, this will have the same effect as if “&key=value” wasn’t included in the query string.

  5. Mark said

    Nice stuff. At the end though, you can improve your string building by axing the string concatenation (which is slow)
    Replace this:

    var toString = "?";
    for (var key in queryStringDictionary) {
    toString += key + "=" +
    queryStringDictionary[key];
    }

    With:

    var toString = ["?"];
    for (var key in queryStringDictionary) {
    toString.push(key + "=" +
    queryStringDictionary[key]);
    }
    toString = toString.join("");

  6. sandrar said

    Hi! I was surfing and found your blog post… nice! I love your blog. :) Cheers! Sandra. R.

  7. I took many of the comment suggestions above and integrated them into a working version of your location object extension, query string. My finished version will handle array assignment with query strings like:
    ?arg[]=val1&arg[]=val2&arr[a]=vala&arr[b]=valb

    I also added the methods: toObject, which returns the query as an object; toArray, which returns the query as an array.

    Here is the code:

    location.query = (function() {

    // The return is a collection of key/value pairs

    var queryStringDictionary = {};

    // Gets the query string, starts with '?'

    var querystring = decodeURIComponent(location.search);

    // document.location.search is empty if no query string

    if (!querystring) {
    return {};
    }

    // Remove the '?' via substring(1)

    querystring = querystring.substring(1);

    // '&' seperates key/value pairs

    var pairs = querystring.split("&");

    // Save the number of key/value pairs as a length property.

    queryStringDictionary.length = pairs.length

    // Load the key/values of the return collection

    for (var i = 0; i -1) {

    // Key is an unnamed array assignment.

    var key = keyValuePair[0].substr(0,
    keyValuePair[0].length-2);

    // If key does not exist create it.

    if (!queryStringDictionary[key]) {
    queryStringDictionary[key] = new Array();
    }

    queryStringDictionary[key].push(keyValuePair[1]);
    }
    else if (keyValuePair[0].match(/^([^\[]+)\[([^\]]+)\]$/)) {

    // Key is a named array assignment.

    key = RegExp.$1;
    index = RegExp.$2;

    if (!queryStringDictionary[key]) {
    queryStringDictionary[key] = new Array();
    }

    // If key does not exist create it.

    queryStringDictionary[key][index]
    = keyValuePair[1];
    }
    else {

    // Key is a string assignment.

    queryStringDictionary[keyValuePair[0]]
    = keyValuePair[1];
    }
    }
    }

    // Define a methods to return the query data.

    // isReserved() returns the true is the key argument is a
    // name reserved for a method or property.

    queryStringDictionary.isReserved = function(key) {
    return (key == "length"
    || key == "toString"
    || key == "toObject"
    || key == "toArray");
    };

    // toString() returns the key/value pairs concatenated.

    queryStringDictionary.toString = function() {

    if (queryStringDictionary.length == 0) {
    return "";
    }

    var toString = "?";

    for (var key in queryStringDictionary) {
    if (!queryStringDictionary.isReserved(key)) {

    var value = queryStringDictionary[key];

    if (value instanceof Array) {
    for (var i in value) {
    if (!isNaN(i)) {
    toString += key +
    "[]=" + value[i];
    }
    else if (typeof(i) == "string"
    || i instanceof String) {
    toString += key +
    "[" + i + "]=" +
    value[i];
    }
    }
    }
    else if (value instanceof String
    || typeof(value) == "string") {
    toString += key + "=" + value;
    }
    }
    }

    return toString;
    };

    // toObject() returns the key/value pairs as an Object with each
    // key a property equal to its corresponding value.

    queryStringDictionary.toObject = function() {

    var toObject = {};

    if (queryStringDictionary.length > 0) {
    for (var key in queryStringDictionary) {
    if (!queryStringDictionary.isReserved(key)) {

    var value = queryStringDictionary[key];

    if (typeof(value) == "string"
    || value instanceof String
    || value instanceof Array) {

    // Add array to the return variable.

    toObject[key] = queryStringDictionary[key];
    }
    }
    }
    }

    return toObject;
    };

    // toArray() returns the key/value pairs in an Array.

    queryStringDictionary.toArray = function() {

    var toArray = [];

    if (queryStringDictionary.length > 0) {
    for (var keyName in queryStringDictionary) {
    if (!queryStringDictionary.isReserved(keyName)) {

    var value = queryStringDictionary[keyName];

    if (typeof(value) == "string"
    || value instanceof String
    || value instanceof Array) {

    // Add array to the return variable.

    toArray.push({ key : keyName, value : queryStringDictionary[keyName]});
    }
    }
    }
    }

    return toArray;
    };

    // Return the key/value dictionary

    return queryStringDictionary;

    })();

  8. I am using a URL parser to parse a query string that looks like this.
    index.cfm/L1-3/L2-6/A-326

    How can I get use your JavaScript function to display this back? Thanks!

  9. Ian said

    Great post, very helpful and being used in my current project. Thanks.

  10. There is nothing wrong with sandbox games, but are simply at the mercy of an evolving genre.
    William Kristol still publishes a poplar conservative magazine, William F
    Buckley’s “National Review” is still published. The Plot:
    In what turns out to be the sixth movie in a series, a war that started because the
    people of Naboo had something they wanted to sell and the Trade
    Federation didn’t want them to sell, drags on, with the Rebel Alliance (who actually aren’t so much rebels as they were pretty much the
    government just 20 years before, but let’s not dwell on that) mounting a last attack on the Empire’s newest weapon — with a brief sidetrip to a desert
    planet to rescue a loved one, in a parallel that’s too obvious to
    miss: Anakin’s trip back to Tatooine to rescue his mom set him further down the path to the Dark Side,
    while Luke’s trip to the same planet to rescue Han poses no moral dilemmas at all (Then again, it’s
    not surprising that a guy who made out with his sister doesn’t have
    many ethical qualms.

RSS feed for comments on this post · TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: