views:

13293

answers:

10

In a web application that makes use of AJAX calls, I need to submit a request but add a parameter to the end of the URL, for example:

Original URL:

http://server/myapp.php?id=10

Resulting URL:

http://server/myapp.php?id=10&enabled=true

Looking for a JavaScript function which parses the URL looking at each parameter, then adds the new parameter or updates the value if one already exists.

A: 

Have you searched for javascript url parsers ? You could make your own, splitting on every &-character, but it's probably easier just to use existing code.

csl
+1  A: 

I had a similar scenario once and I found this article by Peter Bromberg very helpful :

Cerebrus
A: 

I'll have a go. I'd do something like this:

function constructQuery(newkey, newvalue) {
 // the url will be split into three pieces
 var stem = window.location.href; 
 var query = "";
 var anchor = "";

 // split off the anchor
 var anchorindex = stem.lastIndexOf("#");
 if(anchorindex != -1) {
  anchor = stem.substring(anchorindex);
  stem = stem.substring(0, anchorindex);
 }

 // split off the query
 var queryindex = stem.lastIndexOf("?");
 if(queryindex != -1) {
  query = stem.substring(queryindex+1);
  stem = stem.substring(0, queryindex+1);
 }
 else{ stem += "?"; }

 // rewrite the query
 if(query != "") {
  var parameters = query.split("&");
  var newquery = "";
  for(int i = 0; i < parameters.length; ++i) {
   var parameter = parameters[i].split("=");
   if(parameter[0] == newkey) { continue; }
   if(newquery.length > 0) { newquery += "&"; }
   newquery += parameter;
  }
  query = newquery + "&" + newkey + "=" + newvalue;
 }

 // recombine the three pieces
 return stem + query + anchor;
}

I made up this code just then so I hope I've thought through everything but I might've missed something. I don't know if it even compiles, but at least you get the idea.

Ray Hidayat
+13  A: 

A basic implementation which you'll need to adapt would look something like this:

function insertParam(key, value)
{
    key = escape(key); value = escape(value);

    var kvp = document.location.search.substr(1).split('&');

    var i=kvp.length; var x; while(i--) 
    {
     x = kvp[i].split('=');

     if (x[0]==key)
     {
      x[1] = value;
      kvp[i] = x.join('=');
      break;
     }
    }

    if(i<0) {kvp[kvp.length] = [key,value].join('=');}

    //this will reload the page, it's likely better to store this until finished
    document.location.search = kvp.join('&'); 
}

This is approximately twice as fast as a regex or search based solution, but that depends completely on the length of the querystring and the index of any match


the slow regex method I benchmarked against for completions sake (approx +150% slower)

function insertParam2(key,value)
{
    key = escape(key); value = escape(value);

    var s = document.location.search;
    var kvp = key+"="+value;

    var r = new RegExp("(&|\\?)"+key+"=[^\&]*");

    s = s.replace(r,"$1"+kvp);

    if(!RegExp.$1) {s += (s.length>0 ? '&' : '?') + kvp;};

    //again, do what you will here
    document.location.search = s;
}
annakata
thanks again. see http://www.lessanvaezi.com/wp-content/uploads/2009/01/test.html for a basic comparison of the methods
Lessan Vaezi
+1  A: 

This was my own attempt, but I'll use the answer by annakata as it seems much cleaner:

function AddUrlParameter(sourceUrl, parameterName, parameterValue, replaceDuplicates)
{
    if ((sourceUrl == null) || (sourceUrl.length == 0)) sourceUrl = document.location.href;
    var urlParts = sourceUrl.split("?");
    var newQueryString = "";
    if (urlParts.length > 1)
    {
     var parameters = urlParts[1].split("&");
     for (var i=0; (i < parameters.length); i++)
     {
      var parameterParts = parameters[i].split("=");
      if (!(replaceDuplicates && parameterParts[0] == parameterName))
      {
       if (newQueryString == "")
        newQueryString = "?";
       else
        newQueryString += "&";
       newQueryString += parameterParts[0] + "=" + parameterParts[1];
      }
     }
    }
    if (newQueryString == "")
     newQueryString = "?";
    else
     newQueryString += "&";
    newQueryString += parameterName + "=" + parameterValue;

    return urlParts[0] + newQueryString;
}

Also, I found this jQuery plugin from another post on stackoverflow, and if you need more flexibility you could use that: http://plugins.jquery.com/project/query-object

I would think the code would be (haven't tested):

return $.query.parse(sourceUrl).set(parameterName, parameterValue).toString();
Lessan Vaezi
+4  A: 

I have a 'class' that does this and here it is:

function QS(){
    this.qs = {};
    var s = location.search.replace( /^\?|#.*$/g, '' );
    if( s ) {
        var qsParts = s.split('&');
        var i, nv;
        for (i = 0; i < qsParts.length; i++) {
            nv = qsParts[i].split('=');
            this.qs[nv[0]] = nv[1];
        }
    }
}

QS.prototype.add = function( name, value ) {
    if( arguments.length == 1 && arguments[0].constructor == Object ) {
        this.addMany( arguments[0] );
        return;
    }
    this.qs[name] = value;
}

QS.prototype.addMany = function( newValues ) {
    for( nv in newValues ) {
        this.qs[nv] = newValues[nv];
    }
}

QS.prototype.remove = function( name ) {
    if( arguments.length == 1 && arguments[0].constructor == Array ) {
        this.removeMany( arguments[0] );
        return;
    }
    delete this.qs[name];
}

QS.prototype.removeMany = function( deleteNames ) {
    var i;
    for( i = 0; i < deleteNames.length; i++ ) {
        delete this.qs[deleteNames[i]];
    }
}

QS.prototype.getQueryString = function() {
    var nv, q = [];
    for( nv in this.qs ) {
        q[q.length] = nv+'='+this.qs[nv];
    }
    return q.join( '&' );
}

QS.prototype.toString = QS.prototype.getQueryString;

//examples
//instantiation
var qs = new QS;
alert( qs );

//add a sinle name/value
qs.add( 'new', 'true' );
alert( qs );

//add multiple key/values
qs.add( { x: 'X', y: 'Y' } );
alert( qs );

//remove single key
qs.remove( 'new' )
alert( qs );

//remove multiple keys
qs.remove( ['x', 'bogus'] )
alert( qs );

I have overridden the toString method so there is no need to call QS::getQueryString, you can use QS::toString or, as I have done in the examples just rely on the object being coerced into a string.

meouw
+1  A: 

Ok here I compare Two functions, one made by myself (regExp) and another one made by (annakata).

Split array:

function insertParam(key, value)
{
    key = escape(key); value = escape(value);

    var kvp = document.location.search.substr(1).split('&');

    var i=kvp.length; var x; while(i--) 
    {
        x = kvp[i].split('=');

        if (x[0]==key)
        {
                x[1] = value;
                kvp[i] = x.join('=');
                break;
        }
    }

    if(i<0) {kvp[kvp.length] = [key,value].join('=');}

    //this will reload the page, it's likely better to store this until finished
    return "&"+kvp.join('&'); 
}

Regexp method:

function addParameter(param, value)
{
    var regexp = new RegExp("(\\?|\\&)" + param + "\\=([^\\&]*)(\\&|$)");
    if (regexp.test(document.location.search)) 
        return (document.location.search.toString().replace(regexp, function(a, b, c, d)
        {
                return (b + param + "=" + value + d);
        }));
    else 
        return document.location.search+ param + "=" + value;
}

Testing case:

time1=(new Date).getTime();
for (var i=0;i<10000;i++)
{
addParameter("test","test");
}
time2=(new Date).getTime();
for (var i=0;i<10000;i++)
{
insertParam("test","test");
}

time3=(new Date).getTime();

console.log((time2-time1)+" "+(time3-time2));

It seems that even with simplest solution (when regexp use only test and do not enter .replace function) it is still slower than spliting... Well. Regexp is kinda slow but... uhh...

Wilq32
as I mentioned, this is actually comparatively slow - and fwiw, document.location.search is clearer
annakata
+1  A: 

Thank you all for your contribution. I used annakata code and modified to also include the case where there is no query string in the url at all. Hope this would help.

function insertParam(key, value) {
        key = escape(key); value = escape(value);

        var kvp = document.location.search.substr(1).split('&');
        if (kvp == "") {
            document.location.search = key + "=" + value;
        }
        else {

            var i = kvp.length; var x; while (i--) {
                x = kvp[i].split('=');

                if (x[0] == key) {
                    x[1] = value;
                    kvp[i] = x.join('=');
                    break;
                }
            }

            if (i < 0) { kvp[kvp.length] = [key, value].join('='); }

            //this will reload the page, it's likely better to store this until finished
            document.location.search = kvp.join('&');
        }
    }
Mehdi
A: 

hey guys,

am using the solutions provided on this thread but am getting my new parameter and value from a select field eg.

select option 1 = 5 option 2 = 10 option 3 = 15

when a user selects option 2, i call that function to reconstruct my url and append such as:

http://www.blahblah.com/apps/category.php?pg=1&amp;catId=3001&amp;imit=10 but my problem is that the page reloads and causes the selected value to no longer reflect option 2 but instead option 1. How do i resolve this?

Afamee
+1  A: 

Here's a vastly simplified version, making tradeoffs for legibility and fewer lines of code instead of micro-optimized performance (and we're talking about a few miliseconds difference, realistically... due to the nature of this (operating on the current document's location), this will most likely be ran once on a page).

/**
* Add a URL parameter (or changing it if it already exists)
* @param {search} string  this is typically document.location.search
* @param {key}    string  the key to set
* @param {val}    string  value 
*/
var addUrlParam = function(search, key, val){
  var newParam = key + '=' + val,
      params = '?' + newParam;

  // If the "search" string exists, then build params from it
  if (search) {
    // Try to replace an existance instance
    params = search.replace(new RegExp('[\?&]' + key + '[^&]*'), '$1' + newParam);

    // If nothing was replaced, then add the new param to the end
    if (params === search) {
      params += '&' + newParam;
    }
  }

  return params;
};

You would then use this like so:

document.location.pathname + addUrlParam(document.location.search, 'foo', 'bar');
Garrett
thanks for this
mnml