如何添加或更新查询字符串参数?

476
使用Javascript,如果URL中不存在指定参数,则添加查询字符串参数;如果存在指定参数,则更新其当前值。我正在使用jQuery进行客户端开发。

1
似乎我在职业生涯中已经用JavaScript编写了大约100次“parseQueryString”函数。它并不难。关键点是,String#split需要第二个参数来设置最大的分割次数。jQuery的map也会很有帮助。 - jpsimons
这个 Stack Overflow 的帖子也有很多解决方案 https://dev59.com/enNA5IYBdhLWcg3wGJ0V - Dilip Rajkumar
2
有些人认为这个问题的意思是“如何更改地址栏中的URL”,而另一些人则认为是“如何更改任何URL变量”。 - PandaWood
可能是重复的问题 https://dev59.com/KXRB5IYBdhLWcg3w3K4J - User
30个回答

518

我编写了以下函数,它实现了我想要达到的目标:

function updateQueryStringParameter(uri, key, value) {
  var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
  var separator = uri.indexOf('?') !== -1 ? "&" : "?";
  if (uri.match(re)) {
    return uri.replace(re, '$1' + key + "=" + value + '$2');
  }
  else {
    return uri + separator + key + "=" + value;
  }
}

43
如果URI中有哈希标记,这个答案就不适用了——查询字符串必须放在哈希标记前面,否则它们不会被发送到服务器。http://jsfiddle.net/4yXzR/ - Greg
8
在分隔符声明之前加上 var,将变量作用域限制在本地函数中。 - Andrea Salicetti
2
不错!@greg:你是对的 - 看看我的答案,它应该能处理哈希标签。 - Adam
5
我认为你应该在第一行添加"value = encodeURIComponent(value);",否则换行符将无法正确转义。 - Felix
18
这也会处理哈希值: https://gist.github.com/niyazpk/f8ac616f181f6042d1e0 - Niyaz
显示剩余11条评论

358

更新 (2020):URLSearchParams 现在被所有现代浏览器支持

window.location.search 结合使用,URLSearchParams 实用工具非常有用。例如:

if ('URLSearchParams' in window) {
    var searchParams = new URLSearchParams(window.location.search);
    searchParams.set("foo", "bar");
    window.location.search = searchParams.toString();
}

现在,无论foo是否已存在,它都已被设置为bar
但是,对window.location.search的上述分配将导致页面加载,因此如果不希望出现这种情况,请像下面这样使用历史记录API
if ('URLSearchParams' in window) {
    var searchParams = new URLSearchParams(window.location.search)
    searchParams.set("foo", "bar");
    var newRelativePathQuery = window.location.pathname + '?' + searchParams.toString();
    history.pushState(null, '', newRelativePathQuery);
}

现在,您不需要编写自己的正则表达式或逻辑来处理可能存在的查询字符串。

然而,浏览器支持并不好,因为它目前处于实验阶段,仅在最新版本的Chrome、Firefox、Safari、iOS Safari、Android Browser、Android Chrome和Opera中使用。如果您决定使用它,请使用polyfill


在IE11上,您会收到该错误:“无法设置未定义或空引用的属性'URLSearchParams:0.8503766759030615'”。如果作为后备方案不起作用,那么它就不是一个好的polyfill。 - Val
15
更新于2019年:现在大多数浏览器都支持,除了IE和一些小型移动浏览器。新的ungap polyfill可以轻松解决这个问题:https://github.com/ungap/url-search-params - Flion
完美的答案,但它无法处理URL中存在哈希标签的用例。将哈希简单地附加到 newRelativePathQuery 的末尾即可解决问题:var newRelativePathQuery = window.location.pathname + '?' + searchParams.toString() + window.location.hash; - kudlohlavec
4
我发现使用URL比使用URLSearchParams更容易 const url = new URL(window.location); url.searchParams.set("foo", "bar"); window.location = url; - Joe Boris

196

我已经扩展了这个解决方案,并结合另一个我找到的方案,根据用户的输入替换/更新/删除查询字符串参数并考虑URL的锚点。

不提供值将删除该参数,提供值将添加/更新该参数。如果没有提供URL,则会从window.location中获取URL。

function UpdateQueryString(key, value, url) {
    if (!url) url = window.location.href;
    var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"),
        hash;

    if (re.test(url)) {
        if (typeof value !== 'undefined' && value !== null) {
            return url.replace(re, '$1' + key + "=" + value + '$2$3');
        } 
        else {
            hash = url.split('#');
            url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, '');
            if (typeof hash[1] !== 'undefined' && hash[1] !== null) {
                url += '#' + hash[1];
            }
            return url;
        }
    }
    else {
        if (typeof value !== 'undefined' && value !== null) {
            var separator = url.indexOf('?') !== -1 ? '&' : '?';
            hash = url.split('#');
            url = hash[0] + separator + key + '=' + value;
            if (typeof hash[1] !== 'undefined' && hash[1] !== null) {
                url += '#' + hash[1];
            }
            return url;
        }
        else {
            return url;
        }
    }
}

更新

修复了删除查询字符串中第一个参数时出现的错误,重新设计了正则表达式和测试以包括修复。

第二次更新

根据@JarónBarends的建议 - 调整值检查以检查undefined和null以允许设置0值。

第三次更新

修复了直接在井号之前删除查询字符串变量会丢失井号符号的错误。

第四次更新

感谢@rooby指出第一个RegExp对象中的正则表达式优化。由于使用(\? |&)存在问题,因此将初始正则表达式设置为([?&])。

第五次更新

从if / else语句中删除哈希var声明。


5
如果只是检查value的真实性(truthiness),当你将它们的值设置为0时,这会导致从查询字符串中删除变量。因此,你应该使用 if (typeof value !== 'undefined' && value !== null) {} 替换 if (value) {}。请注意保持原文意思不变。 - Jarón Barends
1
在正则表达式中,([?|&]) 应该改为 ([?&])。 - rooby
1
在Chrome版本31.0.1650.63中创建正则表达式时出现“无效的正则异常/无效的组”异常,通过将开头返回为“([?&])”来解决。 - Yonatan Karni
@YonatanKarni 这很有趣,我的快速测试成功了,但在我有时间研究它之前,我会恢复它,谢谢。 - ellemayo
1
虽然这并不重要,只是我有点强迫症,但变量 hash 被声明了两次 ;) - wlingke
显示剩余13条评论

47

您可以使用浏览器的本地URL API以非常简单的方式完成此操作,其中keyvalue分别是您的参数名称和参数值。

const url = new URL(location.href);
url.searchParams.set(key, value);

这将保留URL的所有内容,仅更改或添加一个查询参数。

然后,您可以对url对象进行任何操作。例如:

// Log the URL string.
console.log(url.href);

// Go to the URL.
location.assign(url);

// Go to the URL, but overwrite this history entry.
location.replace(url);

// Same as `location.assign` without reloading.
history.pushState(null, '', url);

// Same as `location.replace` without reloading.
history.replaceState(null, '', url);

46
基于@amateur的答案(现在包括@j_walker_dev评论中的修复),但考虑到url中的哈希标签的评论,我使用以下内容:
function updateQueryStringParameter(uri, key, value) {
  var re = new RegExp("([?&])" + key + "=.*?(&|#|$)", "i");
  if (uri.match(re)) {
    return uri.replace(re, '$1' + key + "=" + value + '$2');
  } else {
    var hash =  '';
    if( uri.indexOf('#') !== -1 ){
        hash = uri.replace(/.*#/, '#');
        uri = uri.replace(/#.*/, '');
    }
    var separator = uri.indexOf('?') !== -1 ? "&" : "?";    
    return uri + separator + key + "=" + value + hash;
  }
}

编辑:修复正则表达式中的[?|&],应该改为[?&],如评论所指出。

编辑:另一种版本支持删除URL参数。我使用value === undefined表示删除。可以根据需要使用value === false或甚至是单独的输入参数。

function updateQueryStringParameter(uri, key, value) {
  var re = new RegExp("([?&])" + key + "=.*?(&|#|$)", "i");
  if( value === undefined ) {
    if (uri.match(re)) {
    return uri.replace(re, '$1$2').replace(/[?&]$/, '').replaceAll(/([?&])&+/g, '$1').replace(/[?&]#/, '#');
  } else {
    return uri;
  }
  } else {
    if (uri.match(re)) {
      return uri.replace(re, '$1' + key + "=" + value + '$2');
  } else {
    var hash =  '';
    if( uri.indexOf('#') !== -1 ){
        hash = uri.replace(/.*#/, '#');
        uri = uri.replace(/#.*/, '');
    }
    var separator = uri.indexOf('?') !== -1 ? "&" : "?";    
    return uri + separator + key + "=" + value + hash;
    }
  }
}

您可以在以下链接中查看其运行效果:https://jsfiddle.net/cdt16wex/


2
更加干净。对于使用 angular ui-router 的用户来说,可以在哈希中有“?”。将 var separator 行移到返回语句的正上方会修复 "/app#/cool?fun=true" 的错误。这将选择“&”作为分隔符,即使尚未有真正的查询字符串参数,仅有客户端参数。 - j_walker_dev
3
在其他答案的评论中指出,[?|&] 应该改为 [?&](否则会匹配 |)。 - bonger
1
是的,"([?|&])" 不太好。请将其修复为 var re = new RegExp("(?|&)" + key + "=.*?(&|#|$)", "i"); 或者 var re = new RegExp("([?&])" + key + "=.*?(&|#|$)", "i");(在其他方面也是一个不错的扩展!) - YakovL
不完全是这样,@jimmykup。但可以很容易地进行调整以实现这一点。 - Adam
1
谢谢,编辑得很好。在 JSFiddle 中,如果我反复删除和添加参数,最终 URL 的样子会变成这样:https://www.example.com/?&&&&#baz。如果我只删除 foo 参数,我会得到这个:https://www.example.com/?&bar=2#baz,即使只有一个参数,其中仍然有 ? 和 &。 - jkupczak
显示剩余7条评论

24

由于现代JavaScript、Node.js和浏览器的支持,我们可以摆脱第三方库旋涡(jquery、query-string等)并使代码更加简洁。

以下是为给定URL添加或更新查询参数的JavaScript(Node.js)和TypeScript版本的函数:

Javascript

const getUriWithParam = (baseUrl, params) => {
  const Url = new URL(baseUrl);
  const urlParams = new URLSearchParams(Url.search);
  for (const key in params) {
    if (params[key] !== undefined) {
      urlParams.set(key, params[key]);
    }
  }
  Url.search = urlParams.toString();
  return Url.toString();
};

console.info('expected: https://example.com/?foo=bar');
console.log(getUriWithParam("https://example.com", {foo: "bar"}));

console.info('expected: https://example.com/slug?foo=bar#hash');
console.log(getUriWithParam("https://example.com/slug#hash", {foo: "bar"}));

console.info('expected: https://example.com/?bar=baz&foo=bar');
console.log(getUriWithParam("https://example.com?bar=baz", {foo: "bar"}));

console.info('expected: https://example.com/?foo=baz&bar=baz');
console.log(getUriWithParam("https://example.com?foo=bar&bar=baz", {foo: "baz"}));

TypeScript


const getUriWithParam = (
  baseUrl: string,
  params: Record<string, any>
): string => {
  const Url = new URL(baseUrl);
  const urlParams: URLSearchParams = new URLSearchParams(Url.search);
  for (const key in params) {
    if (params[key] !== undefined) {
      urlParams.set(key, params[key]);
    }
  }
  Url.search = urlParams.toString();
  return Url.toString();
};

对于React Native

URL在React Native中未实现。因此,您需要事先安装react-native-url-polyfill

对于对象参数

请参见此答案中的第二个解决方案。


1
这应该是最佳选择。 - meYnot
1
IE不受支持。 - gdm

20

15

如果它没有被设置,或者想要用新值更新,可以使用:

window.location.search = 'param=value'; // or param=new_value

顺便说一下,这是简单的Javascript。

编辑

你可能想尝试使用jquery query-object 插件。

window.location.search = jQuery.query.set("param", 5);


5
但这不会保留URL上的任何其他查询字符串参数。 - amateur
你说得对,我道歉。作为更新,我在我的答案中添加了另一个选项 :) - tradyblix
这不起作用,因为它没有重置一个值。 "添加或更新" - Paolo Falomo

14

我知道这个问题已经很老了,而且已经有了很多答案,但是我也想发表一下我的看法。我正在尝试重新发明轮子,因为我曾经使用过目前被接受的答案,但最近在一个项目中,URL片段的处理出了问题。

下面是函数代码。它相当冗长,但是我尽可能地使它具有韧性。如果您有缩短/改进它的建议,我会非常乐意听取。我为它(或其他类似函数)编写了一个小的jsFiddle测试套件。如果一个函数能够通过那里的每一个测试,我认为它应该可以使用。

更新:我发现了一个很酷的函数使用DOM解析URL,所以我在这里加入了这种技术。它使函数变得更短、更可靠。感谢那个函数的作者。

/**
 * Add or update a query string parameter. If no URI is given, we use the current
 * window.location.href value for the URI.
 * 
 * Based on the DOM URL parser described here:
 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
 *
 * @param   (string)    uri     Optional: The URI to add or update a parameter in
 * @param   (string)    key     The key to add or update
 * @param   (string)    value   The new value to set for key
 *
 * Tested on Chrome 34, Firefox 29, IE 7 and 11
 */
function update_query_string( uri, key, value ) {

    // Use window URL if no query string is provided
    if ( ! uri ) { uri = window.location.href; }

    // Create a dummy element to parse the URI with
    var a = document.createElement( 'a' ), 

        // match the key, optional square brackets, an equals sign or end of string, the optional value
        reg_ex = new RegExp( key + '((?:\\[[^\\]]*\\])?)(=|$)(.*)' ),

        // Setup some additional variables
        qs,
        qs_len,
        key_found = false;

    // Use the JS API to parse the URI 
    a.href = uri;

    // If the URI doesn't have a query string, add it and return
    if ( ! a.search ) {

        a.search = '?' + key + '=' + value;

        return a.href;
    }

    // Split the query string by ampersands
    qs = a.search.replace( /^\?/, '' ).split( /&(?:amp;)?/ );
    qs_len = qs.length; 

    // Loop through each query string part
    while ( qs_len > 0 ) {

        qs_len--;

        // Remove empty elements to prevent double ampersands
        if ( ! qs[qs_len] ) { qs.splice(qs_len, 1); continue; }

        // Check if the current part matches our key
        if ( reg_ex.test( qs[qs_len] ) ) {

            // Replace the current value
            qs[qs_len] = qs[qs_len].replace( reg_ex, key + '$1' ) + '=' + value;

            key_found = true;
        }
    }   

    // If we haven't replaced any occurrences above, add the new parameter and value
    if ( ! key_found ) { qs.push( key + '=' + value ); }

    // Set the new query string
    a.search = '?' + qs.join( '&' );

    return a.href;
}

2
这看起来非常不错,测试用例很扎实,使用DOM作为URL解析器。 但我确实发现了两个问题: http://example.com/?param=val& => http://example.com/?param=val&&new=key --双& 和 http://example.com/team => http://example.com/team?new=key 期望的是:team/?new=key 一些重定向/team?new=key到/team/并且查询丢失。 - Timar Ivo Batis
1
谢谢提供失败的测试用例。我更新了代码片段。第一个测试用例很难,你有什么想法来修复函数吗?第二个测试用例应该没有问题,而且已经包含在测试套件中了。也许我没有理解你的意思。请随意 fork 测试代码片段,以便演示你遇到的问题。 - Dominic P
1
@braed,我喜欢这种方法,因为它将繁重的工作留给了浏览器。正如你在这里看到的许多答案一样,这个问题并不像看起来那么简单。解决方案需要覆盖很多特殊情况,其中大部分可以免费利用DOM来实现。 - Dominic P
@DominicP 我很想看一些邊緣案例的例子,以及如何使用DOM來解決它們。我甚至沒有考慮過在這裡創建一個虛擬元素等等。你能給我指點一些閱讀材料嗎? - brad
1
@braed 确定,请查看我在答案中提供的测试套件。这里很多不依赖于DOM的解决方案都会失败,因为它们没有考虑到列出的所有边缘情况。至于阅读方面,可能有益于查看node.js的URL模块。虽然它不是直接相关的,但它可以让您了解URL字符串中涉及的一些复杂性。 - Dominic P
显示剩余3条评论

11

window.location.search是可读/可写的。

然而,修改查询字符串将重定向您所在的页面并从服务器刷新。

如果您尝试的是维护客户端状态(并可能使其可以被书签保存),则应修改URL哈希而不是查询字符串,这样可以保持在同一页上(window.location.hash是可读/可写)。这就是像twitter.com这样的网站使用的方法。

您还需要使后退按钮正常工作,必须将JavaScript事件绑定到哈希更改事件上,一个很好的插件是http://benalman.com/projects/jquery-hashchange-plugin/


5
Twitter不再修改哈希标签!哈希已死,欢迎使用历史API - Alba Mendez
它并没有死亡。我认为您生活在一个魔幻的梦境中,那里旧浏览器兼容性不是问题。对于您而言可能是这样,但对于我们其他人(实际上是大多数人)来说绝对不是如此。也许“濒临死亡”更为适当... - AaronHS
1
一年后,看起来唯一不支持它的半新近浏览器是IE 9。 - Douglas.Sesar

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