views:

79

answers:

6

Hi,

I am trying to replace some value in the query string of the current page using JS. Eg: from category=Old Value to category=New Value. To code looks like this:

var sNewValue = 'New Value';
sQueryString = sQueryString.replace(/category=[^&]+&?/, 'category=' + sNewValue);

It works fine except for values that have ampersands. Luckily all the ampersands are URL encoded, so I know I should discard the amp; before the &. I think I should use lookaheads, but I don't know how. I tried this regular expression, but with no luck: /category=[^&(?!amp;)]+&?/

Thanks

A: 

OK, I think I actually found what you need.

var sNewValue = 'category=New Value&';
sQueryString = sQueryString.replace(/category=([^&]|&)*&/, sNewValue)
ChaosPandion
adam0101
+1  A: 

You don’t need to specify a following & at all:

var sNewValue = 'New Value';
sQueryString = sQueryString.replace(/(^|&)category=[^&]*/, '$1category=' + encodeURIComponent(sNewValue));

Because [^&] does already match any character except &. Furthermore, if category is the last parameter, there even is no following &.

Gumbo
adam0101
@adam0101: URIs don’t know anything about HTML character references because URIs are not HTML. The `Value* with no value, and *cat2* having `blah`.
Gumbo
adam0101
+1  A: 

Why are ampersands being encoded as & instead of %26? Or am I reading your question wrong?

If that's the way it needs to be, it might be easier for you to deal with this query string if you break it into name/value pairs first.

var pairStrings = sQueryString.split(/&(?!amp;)/gi);

Then you can work with each value without worrying if it contains an encoded & or not.

adam0101
bogdanvursu
+1  A: 

You can try:

var sNewValue = 'New Value';
sQueryString = sQueryString.replace(/\bcategory=[^&]*/, 'category=' + sNewValue);

This will replace even if category is preceded by ? as in /foo.php?category=bar

M42
A: 

You don't get lookbehind in JavaScript regular expressions. You do get lookahead, but it is unreliable in IE, so best avoided unless you really know what you're doing.

[^&(?!amp;)]+&

yeah, this doesn't really make any sense. You can't use lookahead in a [] character group: what you're saying here is match characters that aren't &, (, ?, ! , a, m, p, ;, or ).

However you should not see & anyway: you should be working on a plain, unencoded query string, eg. as fetched from location.search. (If you are hacking at HTML markup in a string with regex you've got much, much bigger problems.)

If you are getting the query string from location.search, you'll have a ? on the front if there's any query. So you can match the beginning with either & or ?, and do your regex match on:

location.search.replace(
    /([?&;]category=)[^&;]+/,
    '$1'+encodeURIComponent(sNewValue)
);

note I have also included ; as a possible separator as per HTML4 section B.2.2, and used encodeURIComponent so that invalid characters like spaces and special characters like & itself get properly URL-encoded. Also the character $, which otherwise has special meaning in a regexp replacement string.

This is still quite messy, and doesn't cope with URL-encoded parameter names (ie c%61tegory is a valid alternative way of saying category) or multiple parameters with the same name. If you want to be more robust about it you could dump the regexps and do full query string parsing/reconstruction. See the functions from this answer and do:

var lookup= queryToLookup(location.search);
lookup['category']= ['New value'];
var query= lookupToQuery(lookup);
bobince
+1  A: 

/(\bcategory=)(([^&]|&amp\;)*)/

This includes the many different scenarios:

   var s1 = 'http://www.foobar.com/?category=old&foo=bar';
   var s2 = 'http://www.foobar.com/?category=old';
   var s3 = 'http://www.foobar.com/?foo=bar&category=old';
   var s4 = 'http://www.foobar.com/?foo=bar&category=old&value';
   var s5 = 'http://www.foobar.com/?foo=bar&category=old&value&bar=foo';

   var n  = 'NewVal';

   console.log( s1.replace(/(\bcategory=)(([^&]|&amp\;)*)/, "$1" + n) );
   console.log( s2.replace(/(\bcategory=)(([^&]|&amp\;)*)/, "$1" + n) );
   console.log( s3.replace(/(\bcategory=)(([^&]|&amp\;)*)/, "$1" + n) );
   console.log( s4.replace(/(\bcategory=)(([^&]|&amp\;)*)/, "$1" + n) );
   console.log( s5.replace(/(\bcategory=)(([^&]|&amp\;)*)/, "$1" + n) );

   // s1 output: http://www.foobar.com/?category=NewVal&foo=bar
   // s2 output: http://www.foobar.com/?category=NewVal
   // s3 output: http://www.foobar.com/?foo=bar&category=NewVal
   // s4 output: http://www.foobar.com/?foo=bar&category=NewVal
   // s5 output: http://www.foobar.com/?foo=bar&category=NewVal&bar=foo

You may want to add a global switch.

vol7ron
great examples.
Armando