282

Just wondering if there is anything built-in to Javascript that can take a Form and return the query parameters, eg: "var1=value&var2=value2&arr[]=foo&arr[]=bar..."

I've been wondering this for years.

3

23 Answers 23

429

The URLSearchParams API is available in all modern browsers. For example:

const params = new URLSearchParams({
  var1: "value",
  var2: "value2",
  arr: "foo",
});
console.log(params.toString());
//Prints "var1=value&var2=value2&arr=foo"

Sign up to request clarification or add additional context in comments.

9 Comments

Best answer here imo. Just to add to it, you can do params.append(key, value) later for adding new search params in more complicated scenarios.
Note that undefined and null values are included on the final string: x=null&y=undefined
Also, if you already have an URL object (for example const url = new URL("https://stackoverflow.com")), you can set its query strings url.search = new URLSearchParams({foo: "bar"}) or url.searchParams.append("foo", "bar")
Note that TS will warn about using a plain object, but it works fine nonetheless
Note, it encodes entities differently than encodeURIComponent(): spaces here are encoded as + rather than %20. Not sure if that may be the reason of issues with some services.
|
261

2k20 update: use Josh's solution with URLSearchParams.toString().

Old answer:


Without jQuery

var params = {
    parameter1: 'value_1',
    parameter2: 'value 2',
    parameter3: 'value&3' 
};

var esc = encodeURIComponent;
var query = Object.keys(params)
    .map(k => esc(k) + '=' + esc(params[k]))
    .join('&');

For browsers that don't support arrow function syntax which requires ES5, change the .map... line to

    .map(function(k) {return esc(k) + '=' + esc(params[k]);})

5 Comments

Best solution I've seen yet - very clean and concise. A couple of caveats, though. 1) in the .map(), both k and params[k] should be encoded, e.g. encodeURIComponent(k) and encodeURIComponent(params[k]). 2) You are allowed to have more than one instance of a named parameter in a query string. If you want that capability, you'll have to use an array instead of an object (most applications won't want or need that).
3) Not all browsers will support arrow function syntax (that requires ES5). If you want to support all browsers (and encode the parts), replace the above .map() with .map(function(k) {return encodeURIComponent(k) + '=' + encodeURIComponent(params[k]);})
The & in the third value value&3 is not intentional, right? Or is it just there to demonstrate the encoding is working?
@user1738579 I edited the answer to also include your example for non-ES5.
If you're going the ES6 route, you can also use template strings to get rid of the concat: .map( k => `${esc(k)}=${esc(params[k])}` )
149

If you're using jQuery you might want to check out jQuery.param() http://api.jquery.com/jQuery.param/

Example:

var params = {
    parameter1: 'value1',
    parameter2: 'value2',
    parameter3: 'value3' 
};
var query = $.param(params);
console.log(query);

This will print out:

parameter1=value1&parameter2=value2&parameter3=value3

7 Comments

This is actually the correct answer! The query string for a form is $.param($('#myform').serializeArray()).
@Jesse, that would be the same result as: $('#myform').serialize()
jQuery !== JavaScript
@gphilip Well that's why I started the response with "If you're using jQuery ...". Otherwise if you want to implement it in vanilla JS you can examine the implementation of jQuery.param() here github.com/jquery/jquery/blob/master/src/serialize.js :)
Unlike my answer this one supports nested objects like someStr=value1&someObj[a]=5&someObj[b]=6&someArr[]=1&someArr[]=2
|
59

This doesn't directly answer your question, but here's a generic function which will create a URL that contains query string parameters. The parameters (names and values) are safely escaped for inclusion in a URL.

function buildUrl(url, parameters){
  var qs = "";
  for(var key in parameters) {
    var value = parameters[key];
    qs += encodeURIComponent(key) + "=" + encodeURIComponent(value) + "&";
  }
  if (qs.length > 0){
    qs = qs.substring(0, qs.length-1); //chop off last "&"
    url = url + "?" + qs;
  }
  return url;
}

// example:
var url = "http://example.com/";

var parameters = {
  name: "George Washington",
  dob: "17320222"
};

console.log(buildUrl(url, parameters));
// => http://www.example.com/?name=George%20Washington&dob=17320222

6 Comments

Is there any built-in function similar to this one in one of the populars javascript frameworks like jquery, mootools, etc...?
A more robust native JavaScript solution is at stackoverflow.com/a/1714899/1269037
Weirdest implementation ever, why do you need to initialize a new Array while you actually use it as an object? o,O
@UmutSirin Lol yeah I didn't know a lot about Javascript at the time. xD I think I was treating it like a PHP array. Feel free to refactor if you want.
@Michael Haha, yeah. I just sent an edit. Thank you for the answer!
|
39

Create an URL object and append the values to seachParameters

let stringUrl = "http://www.google.com/search";
let url = new URL(stringUrl);
let params = url.searchParams;
params.append("q", "This is seach query");

console.log(url.toString());

The output will be

http://www.google.com/search?q=This+is+seach+query

Comments

22

With jQuery you can do this by $.param

$.param({ action: 'ship', order_id: 123, fees: ['f1', 'f2'], 'label': 'a demo' })

// -> "action=ship&order_id=123&fees%5B%5D=f1&fees%5B%5D=f2&label=a+demo"

Comments

22

ES2017 (ES8)

Making use of Object.entries(), which returns an array of object's [key, value] pairs. For example, for {a: 1, b: 2} it would return [['a', 1], ['b', 2]]. It is not supported (and won't be) only by IE.

Code:

const buildURLQuery = obj =>
      Object.entries(obj)
            .map(pair => pair.map(encodeURIComponent).join('='))
            .join('&');

Example:

buildURLQuery({name: 'John', gender: 'male'});

Result:

"name=John&gender=male"

1 Comment

14

querystring can help.

So, you can

const querystring = require('querystring')

url += '?' + querystring.stringify(parameters)

2 Comments

For frontend applications you might also consider npmjs.com/package/qs
@RobertPankowecki, yes, qs is better and already in my arsenal, cheers!
10

No, I don't think standard JavaScript has that built in, but Prototype JS has that function (surely most other JS frameworks have too, but I don't know them), they call it serialize.

I can reccomend Prototype JS, it works quite okay. The only drawback I've really noticed it it's size (a few hundred kb) and scope (lots of code for ajax, dom, etc.). Thus if you only want a form serializer it's overkill, and strictly speaking if you only want it's Ajax functionality (wich is mainly what I used it for) it's overkill. Unless you're careful you may find that it does a little too much "magic" (like extending every dom element it touches with Prototype JS functions just to find elements) making it slow on extreme cases.

2 Comments

Just wondering if there's anything built-in. Seems like there should be. I hate prototype, but I'm not holding that against you :)
When JavaScript was designed, Ajax was not yet discovered, therfore parsing a form just to get the querystring (that it would creati itself when submitted) probably did not make much sense. Today it does, tough... Btw, compared to script.aculo.us, prototype is nice. :)
7

If you don't want to use a library, this should cover most/all of the same form element types.

function serialize(form) {
  if (!form || !form.elements) return;

  var serial = [], i, j, first;
  var add = function (name, value) {
    serial.push(encodeURIComponent(name) + '=' + encodeURIComponent(value));
  }

  var elems = form.elements;
  for (i = 0; i < elems.length; i += 1, first = false) {
    if (elems[i].name.length > 0) { /* don't include unnamed elements */
      switch (elems[i].type) {
        case 'select-one': first = true;
        case 'select-multiple':
          for (j = 0; j < elems[i].options.length; j += 1)
            if (elems[i].options[j].selected) {
              add(elems[i].name, elems[i].options[j].value);
              if (first) break; /* stop searching for select-one */
            }
          break;
        case 'checkbox':
        case 'radio': if (!elems[i].checked) break; /* else continue */
        default: add(elems[i].name, elems[i].value); break;
      }
    }
  }

  return serial.join('&');
}

1 Comment

Thanks! I was just facing the same problem as the original poster, and your function was exactly what I needed.
5

You can do that nowadays with FormData and URLSearchParams without the need to loop over anything.

const formData = new FormData(form);
const searchParams = new URLSearchParams(formData);
const queryString = searchParams.toString();

Older browsers will need a polyfill, though.

Comments

5

Might be a bit redundant but the cleanest way i found which builds on some of the answers here:

const params: {
   key1: 'value1',
   key2: 'value2',
   key3: 'value3',
}

const esc = encodeURIComponent;
const query = Object.keys(params)
  .map(k => esc(k) + '=' + esc(params[k]))
  .join('&');

return fetch('my-url', {
  method: 'POST',
  headers: {'Content-Type': 'application/x-www-form-urlencoded'},
  body: query,
})

Source

Comments

5

The UrlSearchParams API is a great suggestion, but I can't believe nobody mentioned the incredibly useful .get and .set methods. They can be used to manipulate the query string and not only they're very easy to use, they also solve a number of issues you might encounter. For example, in my case I wanted to build a query string without duplicate keys. .set solves this problem for you. Quoting from the MDN docs:

URLSearchParams.set() Sets the value associated with a given search parameter to the given value. If there are several values, the others are deleted.

Example (from MDN):

let url = new URL('https://example.com?foo=1&bar=2');
let params = new URLSearchParams(url.search);

// Add a third parameter
params.set('baz', 3);

params.toString(); // "foo=1&bar=2&baz=3"

Alternative, shorter syntax:

let url = new URL('https://example.com?foo=1&bar=2');

// Add a third parameter
url.searchParams.set('baz', 3);

url.searchParams.toString(); // "foo=1&bar=2&baz=3"

3 Comments

The last line should probably be url.searchParams.toString();
Actually, it's still wrong. You changed params.toString(); to url.toString();. In my opinion it should be url.searchParams.toString(); (since the OP wants only the query string, not the whole URL).
Oh, sorry, I guess I should have re-read the question! Fixed now.
3

I'm not entirely certain myself, I recall seeing jQuery did it to an extent, but it doesn't handle hierarchical records at all, let alone in a php friendly way.

One thing I do know for certain, is when building URLs and sticking the product into the dom, don't just use string-glue to do it, or you'll be opening yourself to a handy page breaker.

For instance, certain advertising software in-lines the version string from whatever runs your flash. This is fine when its adobes generic simple string, but however, that's very naive, and blows up in an embarrasing mess for people whom have installed Gnash, as gnash'es version string happens to contain a full blown GPL copyright licences, complete with URLs and <a href> tags. Using this in your string-glue advertiser generator, results in the page blowing open and having imbalanced HTML turning up in the dom.

The moral of the story:

   var foo = document.createElement("elementnamehere"); 
   foo.attribute = allUserSpecifiedDataConsideredDangerousHere; 
   somenode.appendChild(foo); 

Not:

   document.write("<elementnamehere attribute=\"" 
        + ilovebrokenwebsites 
        + "\">" 
        + stringdata 
        + "</elementnamehere>");

Google need to learn this trick. I tried to report the problem, they appear not to care.

1 Comment

Right on. Document.write is so 1995, anyway.
3

I know this is very late answer but works very well...

var obj = {
a:"a",
b:"b"
}

Object.entries(obj).map(([key, val])=>`${key}=${val}`).join("&");

note: object.entries will return key,values pairs

output from above line will be a=a&b=b

Hope its helps someone.

Happy Coding...

1 Comment

it doesn't encode at all
2

As Stein says, you can use the prototype javascript library from http://www.prototypejs.org. Include the JS and it is very simple then, $('formName').serialize() will return what you want!

Comments

2

For those of us who prefer jQuery, you would use the form plugin: http://plugins.jquery.com/project/form, which contains a formSerialize method.

Comments

2

Using URL and URLSearchParams

let url = new URL("https://foo.bar");
url.search = new URLSearchParams({
  customerid: 12345,
  lang: "en"
}).toString();

console.log(url.toString());

Comments

1

You don't actually need a form to do this with Prototype. Just use Object.toQueryString function:

Object.toQueryString({ action: 'ship', order_id: 123, fees: ['f1', 'f2'], 'label': 'a demo' })

// -> 'action=ship&order_id=123&fees=f1&fees=f2&label=a%20demo'

2 Comments

Granted, you answered nearly 10 years ago, but this code is now deprecated.
Such a function would be badass! Why did they deprecate it?
1

Is is probably too late to answer your question.
I had the same question and I didn't like to keep appending strings to create a URL. So, I started using $.param as techhouse explained.
I also found a URI.js library that creates the URLs easily for you. There are several examples that will help you: URI.js Documentation.
Here is one of them:

var uri = new URI("?hello=world");
uri.setSearch("hello", "mars"); // returns the URI instance for chaining
// uri == "?hello=mars"

uri.setSearch({ foo: "bar", goodbye : ["world", "mars"] });
// uri == "?hello=mars&foo=bar&goodbye=world&goodbye=mars"

uri.setSearch("goodbye", "sun");
// uri == "?hello=mars&foo=bar&goodbye=sun"

// CAUTION: beware of arrays, the following are not quite the same
// If you're dealing with PHP, you probably want the latter…
uri.setSearch("foo", ["bar", "baz"]);
uri.setSearch("foo[]", ["bar", "baz"]);`

Comments

1

Remove undefined params 💪�??

    urlParams = obj =>{
        const removeUndefined = JSON.parse(JSON.stringify(obj))
        const result = new URLSearchParams(removeUndefined).toString();
        return result ? `?${result}`: '';
    }
    console.log(urlParams({qwe: undefined, txt: 'asd'})) // '?txt=asd'
    console.log(urlParams({qwe: undefined})) // ''

Comments

0

These answers are very helpful, but i want to add another answer, that may help you build full URL. This can help you concat base url, path, hash and parameters.

var url = buildUrl('http://mywebsite.com', {
        path: 'about',
        hash: 'contact',
        queryParams: {
            'var1': 'value',
            'var2': 'value2',
            'arr[]' : 'foo'
        }
    });
    console.log(url);

You can download via npm https://www.npmjs.com/package/build-url

Demo:

;(function () {
  'use strict';

  var root = this;
  var previousBuildUrl = root.buildUrl;

  var buildUrl = function (url, options) {
    var queryString = [];
    var key;
    var builtUrl;
    var caseChange; 
    
    // 'lowerCase' parameter default = false,  
    if (options && options.lowerCase) {
        caseChange = !!options.lowerCase;
    } else {
        caseChange = false;
    }

    if (url === null) {
      builtUrl = '';
    } else if (typeof(url) === 'object') {
      builtUrl = '';
      options = url;
    } else {
      builtUrl = url;
    }

    if(builtUrl && builtUrl[builtUrl.length - 1] === '/') {
      builtUrl = builtUrl.slice(0, -1);
    } 

    if (options) {
      if (options.path) {
          var localVar = String(options.path).trim(); 
          if (caseChange) {
            localVar = localVar.toLowerCase();
          }
          if (localVar.indexOf('/') === 0) {
              builtUrl += localVar;
          } else {
            builtUrl += '/' + localVar;
          }
      }

      if (options.queryParams) {
        for (key in options.queryParams) {
          if (options.queryParams.hasOwnProperty(key) && options.queryParams[key] !== void 0) {
            var encodedParam;
            if (options.disableCSV && Array.isArray(options.queryParams[key]) && options.queryParams[key].length) {
              for(var i = 0; i < options.queryParams[key].length; i++) {
                encodedParam = encodeURIComponent(String(options.queryParams[key][i]).trim());
                queryString.push(key + '=' + encodedParam);
              }
            } else {              
              if (caseChange) {
                encodedParam = encodeURIComponent(String(options.queryParams[key]).trim().toLowerCase());
              }
              else {
                encodedParam = encodeURIComponent(String(options.queryParams[key]).trim());
              }
              queryString.push(key + '=' + encodedParam);
            }
          }
        }
        builtUrl += '?' + queryString.join('&');
      }

      if (options.hash) {
        if(caseChange)
            builtUrl += '#' + String(options.hash).trim().toLowerCase();
        else
            builtUrl += '#' + String(options.hash).trim();
      }
    } 
    return builtUrl;
  };

  buildUrl.noConflict = function () {
    root.buildUrl = previousBuildUrl;
    return buildUrl;
  };

  if (typeof(exports) !== 'undefined') {
    if (typeof(module) !== 'undefined' && module.exports) {
      exports = module.exports = buildUrl;
    }
    exports.buildUrl = buildUrl;
  } else {
    root.buildUrl = buildUrl;
  }
}).call(this);


var url = buildUrl('http://mywebsite.com', {
		path: 'about',
		hash: 'contact',
		queryParams: {
			'var1': 'value',
			'var2': 'value2',
			'arr[]' : 'foo'
		}
	});
	console.log(url);

Comments

0

var params = { width:1680, height:1050 };
var str = jQuery.param( params );

console.log(str)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.