使用JavaScript向URL添加参数

502
在使用AJAX调用的Web应用程序中,我需要提交一个请求,但是需要将参数添加到URL的末尾,例如:
原始URL:

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

生成的URL:

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

寻找一种JavaScript函数,它可以解析URL的每个参数,然后添加新参数或更新已存在参数的值。

你有没有搜索过JavaScript URL解析器?你可以自己制作,按每个“&”字符进行分割,但使用现有的代码可能更容易。 - csl
1
我曾经遇到过类似的情况,我发现Peter Bromberg的这篇文章非常有帮助: - Cerebrus
2
window.history.pushState('page2', '标题', document.location+'/page2.php');可以在不加载页面的情况下完成您的工作。 - Rutunj sheladiya
2
这个问题在这里有更好的答案 https://dev59.com/Q2w05IYBdhLWcg3w6F8w#6954277 - rkb
难以置信,JS 这种可怜的语言居然没有原生支持这个功能。 - bohr
这是一个非常好用且轻量级的库:https://medialize.github.io/URI.js/ - Piotr Kowalski
37个回答

697

5
我想在不带值的情况下添加查询参数,例如将XML添加到 http://foo.bar/?x=1&y=2,以便最终结果是 http://foo.bar/?x=1&y=2&XML。这可能吗? 我尝试过url.searchParams.set('XML');url.searchParams.set('XML', null);url.searchParams.set('XML', undefined);,但在Chrome 63中都不起作用。 - Abdull
41
很遗憾,IE不支持它。 - spedy
6
iOS现在支持URLSearchParams(自@kanji评论后9天)。 - MJeffryes
14
好消息 @MJeffryes! 但仍不支持IE和Netscape。 - Vianney Bajart
7
url.searchParams 是对一个只读的 URLSearchParams 对象的引用。这意味着你不能这样做:url.searchParam = new URLSearchParam();但你可以使用 .set() 方法直接修改对象内容,就像这样:const a = {b: 0}你不能修改引用本身:a = {b: 1}但是你可以直接在原地修改对象的属性:a.b = 1在你的 JS 控制台中试一下!;-) - Vianney Bajart
显示剩余9条评论

246
您可以使用URLSearchParams
const urlParams = new URLSearchParams(window.location.search);

urlParams.set('order', 'date');

window.location.search = urlParams;

.set方法的第一个参数是键,第二个参数是值。

注意:Internet Explorer的任何版本都不支持此功能(但在Edge中支持)


6
喜欢这个简单易用的特点,以及它使用本地浏览器方法的方式。 - Juan Solano
19
这是正确答案,应该被投票置顶。这个新的API是URL处理的权威。 - Anthony
4
IE还有存在的必要吗?现在已经有Edge了。 - amarinediary
3
如果你的目标是政府部门,那么IE仍然很重要(2021年6月)。 - Marcos Buarque
2
这是正确的,但需要在最后一行的UrlParams中添加.toString()。 - Dmitriy
显示剩余7条评论

237

一个基本的实现,您需要进行调整,看起来像这样:

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

    // kvp looks like ['key1=value1', 'key2=value2', ...]
    var kvp = document.location.search.substr(1).split('&');
    let i=0;

    for(; i<kvp.length; i++){
        if (kvp[i].startsWith(key + '=')) {
            let pair = kvp[i].split('=');
            pair[1] = value;
            kvp[i] = pair.join('=');
            break;
        }
    }

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

    // can return this or...
    let params = kvp.join('&');

    // reload page with new params
    document.location.search = params;
}

相比使用正则表达式或搜索方法,这个方法大约快两倍,但这完全取决于查询字符串的长度和匹配项的索引。


出于完整性考虑,我进行了基准测试并与较慢的正则表达式方法进行了比较(大约慢150%)

function insertParam2(key,value)
{
    key = encodeURIComponent(key); value = encodeURIComponent(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;
}

17
使用escape()函数来转义URL参数是错误的。当值中包含"+"时,该函数会出现错误。您应该使用encodeURIComponent()函数代替。完整讨论请参考:http://xkr.us/articles/javascript/encode-compare - Antonin Hildebrand
7
我正在调用这个函数,但页面一直在不停地重新加载。请帮帮我! - sumit
8
当URL中没有参数时,您会得到一个?&param=value。 - Roy Toledo
10
你应该使用 encodeURIComponent 而不是 encodeURI,否则在你的名称或值中包含 =& 等字符会破坏 URI。 - Adrian Pronk
3
因为从未修复的漏洞,这个被downvoted了 :) - Daij-Djan
显示剩余8条评论

68

这是一个非常简单的解决方案。它不控制参数的存在,并且不更改现有的值。它将您的参数添加到末尾,因此您可以在后端代码中获取最新的值。

function addParameterToURL(param){
    _url = location.href;
    _url += (_url.split('?')[1] ? '&':'?') + param;
    return _url;
}

9
在处理URL时,也要考虑到其中的“#”符号。以下是一些代码片段:url = (url.indexOf("?") != -1 ? url.split("?")[0]+"?"+part+"&"+url.split("?")[1] : (url.indexOf("#") != -1 ? url.split("#")[0]+"?"+part+"#"+ url.split("#")[1] : url+'?'+part)); - kanzure
3
@kanzure 这玩意看起来非常邪恶,但正是我想要的。谢谢。 - Chad von Nau
1
修复它。此函数不检查参数是否已经提供。我测试过了,得到了https://localhost/preview/inventory.html?sortci=priceASC&sortci=priceASC&sortci=stitle - Jay
@Jay,为什么需要修复?检查你的假设。重复的查询字符串参数是合法的,就像重复的表单字段一样。这就是HTML中复选框列表的实现方式,如果表单方法为get,则会通过查询字符串传递。 - stephen
4
我明白了 :) 原来我在2016年还是个愚蠢的孩子 :) - Jay
显示剩余2条评论

67

感谢大家的贡献。我使用了annakata的代码,并进行了修改,以便在URL中根本没有查询字符串的情况下也可以使用。

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('&');
        }
    }

3
建议使用encodeURIComponent函数替换escape(key)escape(value) - kolobok
5
这句话应该实际检查 if (kvp.length==1 && kvp[0]="") 吗? - Charles L.
@CharlesL。更好的写法是 if (document.location.search) - Lyubimov Roman

35

这是一个大幅简化的版本,为了易读性和减少代码行数而做出了一些权衡,而非微小优化性能(实际上我们在谈论几毫秒的差异……由于操作当前文档位置的特性,这很可能只会在页面上运行一次)。

/**
* 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;
};

然后你可以像这样使用它:

document.location.pathname + addUrlParam(document.location.search, 'foo', 'bar');

2
+1 我喜欢你可以指定除document.location.search以外的东西。 - Muhd
1
+1 非常棒,我同意在这种情况下易读性比微观优化更重要,我很高兴看到有人采取了这种方法。@Muhd 也许当你写下那个评论时,那是JS引擎中的一个错误,但我刚刚测试了在这种情况下的'$1',它可以正常工作。 - JMTyler
非常感谢您提供的解决方案,但是如果我想替换一个值并且已经有一个参数存在,我也会遇到困难。它会写入 $1 而不是 &。 - Codebryo
2
Garrett:你能修正你的答案,让它正确地替换现有参数吗?只需要在 RegExp 中用 ([\?&]) 替换 [\?&] 就行了(我本来可以自己编辑的,但不确定社区政策是否允许修复答案中的错误)。 - opyh
这个程序无法处理像"domain.tld#anchor"这样的URL;它会在锚点后面添加"&"。 - user706420

31

URL类内置了一个函数,您可以使用它轻松处理查询字符串键/值参数:

const url = new URL(window.location.href);
// url.searchParams has several function, we just use `set` function
// to set a value, if you just want to append without replacing value
// let use `append` function

url.searchParams.set('key', 'value');

console.log(url.search) // <== '?key=value'

// if window.location.href has already some qs params this `set` function
// modify or append key/value in it

了解更多有关searchParams 函数的信息。

URL 在 IE 中不受支持,请查看兼容性


1
根据mdn,“url.searchParams”是只读的。 - Nicolas Appriou
@NicolasAppriou,好的,这里有任何违反你提到的链接的代码吗? - AmerllicA
@MuhammedMoussa,感谢您的出色编辑,真的非常感谢。但是兄弟,IE是什么? - AmerllicA

22

/**
* Add a URL parameter 
* @param {string} url 
* @param {string} param the key to set
* @param {string} value 
*/
var addParam = function(url, param, value) {
   param = encodeURIComponent(param);
   var a = document.createElement('a');
   param += (value ? "=" + encodeURIComponent(value) : ""); 
   a.href = url;
   a.search += (a.search ? "&" : "") + param;
   return a.href;
}

/**
* Add a URL parameter (or modify if already exists)
* @param {string} url 
* @param {string} param the key to set
* @param {string} value 
*/
var addOrReplaceParam = function(url, param, value) {
   param = encodeURIComponent(param);
   var r = "([&?]|&amp;)" + param + "\\b(?:=(?:[^&#]*))*";
   var a = document.createElement('a');
   var regex = new RegExp(r);
   var str = param + (value ? "=" + encodeURIComponent(value) : ""); 
   a.href = url;
   var q = a.search.replace(regex, "$1"+str);
   if (q === a.search) {
      a.search += (a.search ? "&" : "") + str;
   } else {
      a.search = q;
   }
   return a.href;
}

url = "http://www.example.com#hashme";
newurl = addParam(url, "ciao", "1");
alert(newurl);

请注意,在将参数添加到查询字符串之前应进行编码。 http://jsfiddle.net/48z7z4kx/

小内存泄漏 - 在返回之前,您需要删除添加到文档中的锚元素。 - hemp
@freedev,不好意思,我不确定。;) 我很难找到一个权威的来源,但证据表明你是正确的。由于创建的元素从未附加到DOM上,一旦变量超出作用域,它应该被清除。当涉及到副作用时,DOM API 的设计并不特别好,所以我过于谨慎。 - hemp
该解决方案会给没有路径的URL添加尾随斜杠,并重新排序不必要和可能不希望出现的参数。它还会附加而不是替换没有值的现有参数(例如http://abc.def/?a&b&b)。 - Adam Leggett
@AdamLeggett 感谢您指出请求参数没有值时发生的错误,我已经修复了我的答案。如果您所说的其余奇怪行为是正确的,那么这些行为通常是由HTML锚点对象生成的,这通常是标准浏览器组件。我建议再次仔细检查,因为浏览器很少添加不必要或不良行为。 - freedev
1
addParam函数的目的是多次添加参数,也许你应该知道即使是相同的值,同一个参数也可以被多次传递。https://dev59.com/RWAg5IYBdhLWcg3wBXAR - freedev
显示剩余6条评论

20

我有一个做这件事的“类”,以下是它:

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 );
我已经重写了toString方法,因此无需调用 QS::getQueryString,您可以使用 QS::toString,或者像我在示例中所做的那样,仅依赖于将对象强制转换为字符串。

16

如果您有一个包含URL的字符串,想要添加参数进行修饰,您可以尝试这个一行代码:

urlstring += ( urlstring.match( /[\?]/g ) ? '&' : '?' ) + 'param=value';
这意味着?将成为参数的前缀,但如果urlstring中已经有?,则&将成为前缀。如果您没有在代码中硬编码的参数,而是在paramvariable中,或者其中包含有趣的字符,我也建议使用encodeURI(paramvariable)。请参阅JavaScript URL 编码了解encodeURI函数的用法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接