I am trying to develop a Video Calling/Conferencing application using WebRTC and node.js.
Right now there is no facility to control bandwidth during during video call. Is there any way to control/reduce bandwidth.
(like I want make whole my web application to work on 150 kbps while video conferencing).
Any suggestions are highly appreciated.
Thanks in advance.
Try this demo. You can inject bandwidth attributes (b=AS) in the session descriptions:
audioBandwidth = 50;
videoBandwidth = 256;
function setBandwidth(sdp) {
sdp = sdp.replace(/a=mid:audio\r\n/g, 'a=mid:audio\r\nb=AS:' + audioBandwidth + '\r\n');
sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + videoBandwidth + '\r\n');
return sdp;
}
// ----------------------------------------------------------
peer.createOffer(function (sessionDescription) {
sessionDescription.sdp = setBandwidth(sessionDescription.sdp);
peer.setLocalDescription(sessionDescription);
}, null, constraints);
peer.createAnswer(function (sessionDescription) {
sessionDescription.sdp = setBandwidth(sessionDescription.sdp);
peer.setLocalDescription(sessionDescription);
}, null, constraints);
b=AS is already present in sdp for data m-line; its default value is 50.
Updated at Sept 23, 2015
Here is a library that provides full control over both audio/video tracks' bitrates:
// here is how to use it
var bandwidth = {
screen: 300, // 300kbits minimum
audio: 50, // 50kbits minimum
video: 256 // 256kbits (both min-max)
};
var isScreenSharing = false;
sdp = BandwidthHandler.setApplicationSpecificBandwidth(sdp, bandwidth, isScreenSharing);
sdp = BandwidthHandler.setVideoBitrates(sdp, {
min: bandwidth.video,
max: bandwidth.video
});
sdp = BandwidthHandler.setOpusAttributes(sdp);
Here is the library code. Its quite big but it works!
// BandwidthHandler.js
var BandwidthHandler = (function() {
function setBAS(sdp, bandwidth, isScreen) {
if (!!navigator.mozGetUserMedia || !bandwidth) {
return sdp;
}
if (isScreen) {
if (!bandwidth.screen) {
console.warn('It seems that you are not using bandwidth for screen. Screen sharing is expected to fail.');
} else if (bandwidth.screen < 300) {
console.warn('It seems that you are using wrong bandwidth value for screen. Screen sharing is expected to fail.');
}
}
// if screen; must use at least 300kbs
if (bandwidth.screen && isScreen) {
sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, '');
sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + bandwidth.screen + '\r\n');
}
// remove existing bandwidth lines
if (bandwidth.audio || bandwidth.video || bandwidth.data) {
sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, '');
}
if (bandwidth.audio) {
sdp = sdp.replace(/a=mid:audio\r\n/g, 'a=mid:audio\r\nb=AS:' + bandwidth.audio + '\r\n');
}
if (bandwidth.video) {
sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + (isScreen ? bandwidth.screen : bandwidth.video) + '\r\n');
}
return sdp;
}
// Find the line in sdpLines that starts with |prefix|, and, if specified,
// contains |substr| (case-insensitive search).
function findLine(sdpLines, prefix, substr) {
return findLineInRange(sdpLines, 0, -1, prefix, substr);
}
// Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix|
// and, if specified, contains |substr| (case-insensitive search).
function findLineInRange(sdpLines, startLine, endLine, prefix, substr) {
var realEndLine = endLine !== -1 ? endLine : sdpLines.length;
for (var i = startLine; i < realEndLine; ++i) {
if (sdpLines[i].indexOf(prefix) === 0) {
if (!substr ||
sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1) {
return i;
}
}
}
return null;
}
// Gets the codec payload type from an a=rtpmap:X line.
function getCodecPayloadType(sdpLine) {
var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+');
var result = sdpLine.match(pattern);
return (result && result.length === 2) ? result[1] : null;
}
function setVideoBitrates(sdp, params) {
params = params || {};
var xgoogle_min_bitrate = params.min;
var xgoogle_max_bitrate = params.max;
var sdpLines = sdp.split('\r\n');
// VP8
var vp8Index = findLine(sdpLines, 'a=rtpmap', 'VP8/90000');
var vp8Payload;
if (vp8Index) {
vp8Payload = getCodecPayloadType(sdpLines[vp8Index]);
}
if (!vp8Payload) {
return sdp;
}
var rtxIndex = findLine(sdpLines, 'a=rtpmap', 'rtx/90000');
var rtxPayload;
if (rtxIndex) {
rtxPayload = getCodecPayloadType(sdpLines[rtxIndex]);
}
if (!rtxIndex) {
return sdp;
}
var rtxFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + rtxPayload.toString());
if (rtxFmtpLineIndex !== null) {
var appendrtxNext = '\r\n';
appendrtxNext += 'a=fmtp:' + vp8Payload + ' x-google-min-bitrate=' + (xgoogle_min_bitrate || '228') + '; x-google-max-bitrate=' + (xgoogle_max_bitrate || '228');
sdpLines[rtxFmtpLineIndex] = sdpLines[rtxFmtpLineIndex].concat(appendrtxNext);
sdp = sdpLines.join('\r\n');
}
return sdp;
}
function setOpusAttributes(sdp, params) {
params = params || {};
var sdpLines = sdp.split('\r\n');
// Opus
var opusIndex = findLine(sdpLines, 'a=rtpmap', 'opus/48000');
var opusPayload;
if (opusIndex) {
opusPayload = getCodecPayloadType(sdpLines[opusIndex]);
}
if (!opusPayload) {
return sdp;
}
var opusFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + opusPayload.toString());
if (opusFmtpLineIndex === null) {
return sdp;
}
var appendOpusNext = '';
appendOpusNext += '; stereo=' + (typeof params.stereo != 'undefined' ? params.stereo : '1');
appendOpusNext += '; sprop-stereo=' + (typeof params['sprop-stereo'] != 'undefined' ? params['sprop-stereo'] : '1');
if (typeof params.maxaveragebitrate != 'undefined') {
appendOpusNext += '; maxaveragebitrate=' + (params.maxaveragebitrate || 128 * 1024 * 8);
}
if (typeof params.maxplaybackrate != 'undefined') {
appendOpusNext += '; maxplaybackrate=' + (params.maxplaybackrate || 128 * 1024 * 8);
}
if (typeof params.cbr != 'undefined') {
appendOpusNext += '; cbr=' + (typeof params.cbr != 'undefined' ? params.cbr : '1');
}
if (typeof params.useinbandfec != 'undefined') {
appendOpusNext += '; useinbandfec=' + params.useinbandfec;
}
if (typeof params.usedtx != 'undefined') {
appendOpusNext += '; usedtx=' + params.usedtx;
}
if (typeof params.maxptime != 'undefined') {
appendOpusNext += '\r\na=maxptime:' + params.maxptime;
}
sdpLines[opusFmtpLineIndex] = sdpLines[opusFmtpLineIndex].concat(appendOpusNext);
sdp = sdpLines.join('\r\n');
return sdp;
}
return {
setApplicationSpecificBandwidth: function(sdp, bandwidth, isScreen) {
return setBAS(sdp, bandwidth, isScreen);
},
setVideoBitrates: function(sdp, params) {
return setVideoBitrates(sdp, params);
},
setOpusAttributes: function(sdp, params) {
return setOpusAttributes(sdp, params);
}
};
})();
Here is how to set advance opus bitrate parameters:
sdp = BandwidthHandler.setOpusAttributes(sdp, {
'stereo': 0, // to disable stereo (to force mono audio)
'sprop-stereo': 1,
'maxaveragebitrate': 500 * 1024 * 8, // 500 kbits
'maxplaybackrate': 500 * 1024 * 8, // 500 kbits
'cbr': 0, // disable cbr
'useinbandfec': 1, // use inband fec
'usedtx': 1, // use dtx
'maxptime': 3
});
A more up-to-date answer
const videobitrate = 20000;
var offer = pc.localDescription;
// Set bandwidth for video
offer.sdp = offer.sdp.replace(/(m=video.*\r\n)/g, `$1b=AS:${videobitrate}\r\n`);
pc.setLocalDescription(offer);
Explanation: a=mid:video is not a guaranteed tag. For receive only video, you might not see it or see a=mid:0. Generally it's a better bet to look for the m=video xxxx xxxx (or similar audio) tag and append the bandwidth parameters underneath
Not sure if this helps, but you can limit the video resolution from getUserMedia with constraints: see demo at simpl.info/getusermedia/constraints/.
My answer is not for node.js, but maybe someone stuck with controlling webrtc bandwidth while developing a native phone app (iOS, android).
So, at least in version GoogleWebRTC (1.1.31999) for iOS and org.webrtc:google-webrtc:1.0.22672 for android there is method in PeerConnection instance.
For iOS:
let updateBitrateSuccessful = pc.setBweMinBitrateBps(300000, currentBitrateBps: 1000000, maxBitrateBps: 3000000)
print("Update rtc connection bitrate " + (updateBitrateSuccessful ? "successful" : "failed"))
Respectively, for Android:
boolean updateBitrateSuccessful = pc.setBitrate(300000, 1000000, 3000000);
Log.d("AppLog", "Update rtc connection bitrate " + (updateBitrateSuccessful ? "successful" : "failed"));
It depends on what SFU media server you're using. But in short, your media server needs to tell the client browser what maximum bitrate it should send, by setting the bandwidth attribute in the answer SDP, as well as in the REMB message it periodically sends.
The REMB (receiver estimated maximum bitrate) applies separately to audio and video streams (at least on desktop Chrome and Firefox that I tested). So if REMB is set to 75kps and you have one audio and one video stream, then each will confine to 75kps for a total transport bitrate of 150kps. You should use chrome://webrtc-internals to test and verify this.
If you are using OPUS as the audio codec, you can control the audio bandwidth separately by setting the maxaveragebitrate attribute in the answer SDP. Setting this attribute will override the REMB value (verified on Chrome). So you can set audio bitrate to 16kps and the video bitrate (via REMB) to 134kps for a combined transport bitrate of 150.
Note that the REMB is sent by your server, so your server needs to support this. The other SDP attributes can be manipulated on the client side by modifying the answer SDP that you receive, right before passing it to setRemoteDescription().
This is my limited understanding based on online research. It's not based on deep knowledge of the technology stack, so please take it with a grain of salt.
You should also be able to use bandwidth constraints on the stream (see this demo), but it doesn't appear to be working, even in the latest canary (29.0.1529.3).
There's some discussion of the SDP-based approach on the discuss-webrtc mailing list, which links to WebRTC bug 1846.
I did it Yesterday and it works like a charm! in my case, it was needed to prevent slow and old phones get freeze during a videocall! have a look
function handle_offer_sdp(offer) {
let sdp = offer.sdp.split('\r\n');//convert to an concatenable array
let new_sdp = '';
let position = null;
sdp = sdp.slice(0, -1); //remove the last comma ','
for(let i = 0; i < sdp.length; i++) {//look if exists already a b=AS:XXX line
if(sdp[i].match(/b=AS:/)) {
position = i; //mark the position
}
}
if(position) {
sdp.splice(position, 1);//remove if exists
}
for(let i = 0; i < sdp.length; i++) {
if(sdp[i].match(/m=video/)) {//modify and add the new lines for video
new_sdp += sdp[i] + '\r\n' + 'b=AS:' + '128' + '\r\n';
}
else {
if(sdp[i].match(/m=audio/)) { //modify and add the new lines for audio
new_sdp += sdp[i] + '\r\n' + 'b=AS:' + 64 + '\r\n';
}
else {
new_sdp += sdp[i] + '\r\n';
}
}
}
return new_sdp; //return the new sdp
}
pc.createOffer(function(offer) {
offer.sdp = handle_offer_sdp(offer); //invoke function saving the new sdp
pc.setLocalDescription(offer);
}, function(error) {
console.log('error -> ' + error);
});
I recommend to change value of maxBitrate property as described here https://stackoverflow.com/a/71223675/1199820
Check this, this works for me.
Control your bitrate via getSenders(), after peer is connected then you can set your maximum bitrate.
This method allow you to control bitrate without renegotiation. so,
you can change the streaming quality during a call
//bandwidth => "unlimited", 75 kbps, 250 kbps, 1000 kbps, 2000 kbps
var bandwidth = 75;
const sender = pc1.getSenders()[0];
const parameters = sender.getParameters();
if (!parameters.encodings) {
parameters.encodings = [{}];
}
if (bandwidth === 'unlimited') {
delete parameters.encodings[0].maxBitrate;
} else {
parameters.encodings[0].maxBitrate = bandwidth * 1000;
}
sender.setParameters(parameters)
.then(() => {
// on success
})
.catch(e => console.error(e));
Reference code & demo
With javascript how can I add a query string parameter to the url if not present or if it present, update the current value? I am using jquery for my client side development.
I wrote the following function which accomplishes what I want to achieve:
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;
}
}
Update (2020): URLSearchParams is now supported by all modern browsers.
The URLSearchParams utility can be useful for this in combination with window.location.search. For example:
if ('URLSearchParams' in window) {
var searchParams = new URLSearchParams(window.location.search);
searchParams.set("foo", "bar");
window.location.search = searchParams.toString();
}
Now foo has been set to bar regardless of whether or not it already existed.
However, the above assignment to window.location.search will cause a page load, so if that's not desirable use the History API as follows:
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);
}
Now you don't need to write your own regex or logic to handle the possible existence of query strings.
However, browser support is poor as it's currently experimental and only in use in recent versions of Chrome, Firefox, Safari, iOS Safari, Android Browser, Android Chrome and Opera. Use with a polyfill if you do decide to use it.
I have expanded the solution and combined it with another that I found to replace/update/remove the querystring parameters based on the users input and taking the urls anchor into consideration.
Not supplying a value will remove the parameter, supplying one will add/update the parameter. If no URL is supplied, it will be grabbed from window.location
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;
}
}
}
Update
There was a bug when removing the first parameter in the querystring, I have reworked the regex and test to include a fix.
Second Update
As suggested by #JarónBarends - Tweak value check to check against undefined and null to allow setting 0 values
Third Update
There was a bug where removing a querystring variable directly before a hashtag would lose the hashtag symbol which has been fixed
Fourth Update
Thanks #rooby for pointing out a regex optimization in the first RegExp object.
Set initial regex to ([?&]) due to issue with using (\?|&) found by #YonatanKarni
Fifth Update
Removing declaring hash var in if/else statement
Based on #amateur's answer (and now incorporating the fix from #j_walker_dev comment), but taking into account the comment about hash tags in the url I use the following:
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;
}
}
Edited to fix [?|&] in regex which should of course be [?&] as pointed out in the comments
Edit: Alternative version to support removing URL params as well. I have used value === undefined as the way to indicate removal. Could use value === false or even a separate input param as wanted.
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;
}
}
}
See it in action at https://jsfiddle.net/cdt16wex/
You can use the browser's native URL API to do this in a fairly simple way, where key and value are your parameter name and parameter value respectively.
const url = new URL(location.href);
url.searchParams.set(key, value);
history.pushState(null, '', url);
This will preserve everything about the URL and only change or add the one query param. You can also use replaceState instead of pushState if you don't want it to create a new browser history entry.
Thanks to modern javascript, node.js and browsers support, we can get out of 3rd-party library whirlpool (jquery, query-string etc.) and DRY ourselves.
Here are javascript(node.js) and typescript version for a function that adds or updates query params of given url:
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();
};
For React Native
URL is not implemented in React Native. So you have to install react-native-url-polyfill beforehand.
For object params
See the second solution in this answer
Here is my library to do that: https://github.com/Mikhus/jsurl
var u = new Url;
u.query.param='value'; // adds or replaces the param
alert(u)
If it's not set or want to update with a new value you can use:
window.location.search = 'param=value'; // or param=new_value
This is in simple Javascript, by the way.
EDIT
You may want to try using the jquery query-object plugin
window.location.search =
jQuery.query.set("param", 5);
I realize this question is old and has been answered to death, but here's my stab at it. I'm trying to reinvent the wheel here because I was using the currently accepted answer and the mishandling of URL fragments recently bit me in a project.
The function is below. It's quite long, but it was made to be as resilient as possible. I would love suggestions for shortening/improving it. I put together a small jsFiddle test suite for it (or other similar functions). If a function can pass every one of the tests there, I say it's probably good to go.
Update: I came across a cool function for using the DOM to parse URLs, so I incorporated that technique here. It makes the function shorter and more reliable. Props to the author of that function.
/**
* 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;
}
window.location.search is read/write.
However - modifying the query string will redirect the page you're on and cause a refresh from the server.
If what you're attempting to do is maintain client side state (and potentially make it bookmark-able), you'll want to modify the URL hash instead of the query string, which keeps you on the same page (window.location.hash is read/write). This is how web sites like twitter.com do this.
You'll also want the back button to work, you'll have to bind javascript events to the hash change event, a good plugin for that is http://benalman.com/projects/jquery-hashchange-plugin/
Here's my approach: The location.params() function (shown below) can be used as a getter or setter. Examples:
Given the URL is http://example.com/?foo=bar&baz#some-hash,
location.params() will return an object with all the query parameters: {foo: 'bar', baz: true}.
location.params('foo') will return 'bar'.
location.params({foo: undefined, hello: 'world', test: true}) will change the URL to http://example.com/?baz&hello=world&test#some-hash.
Here is the params() function, which can optionally be assigned to the window.location object.
location.params = function(params) {
var obj = {}, i, parts, len, key, value;
if (typeof params === 'string') {
value = location.search.match(new RegExp('[?&]' + params + '=?([^&]*)[&#$]?'));
return value ? value[1] : undefined;
}
var _params = location.search.substr(1).split('&');
for (i = 0, len = _params.length; i < len; i++) {
parts = _params[i].split('=');
if (! parts[0]) {continue;}
obj[parts[0]] = parts[1] || true;
}
if (typeof params !== 'object') {return obj;}
for (key in params) {
value = params[key];
if (typeof value === 'undefined') {
delete obj[key];
} else {
obj[key] = value;
}
}
parts = [];
for (key in obj) {
parts.push(key + (obj[key] === true ? '' : '=' + obj[key]));
}
location.search = parts.join('&');
};
I know this is quite old but i want to fires my working version in here.
function addOrUpdateUrlParam(uri, paramKey, paramVal) {
var re = new RegExp("([?&])" + paramKey + "=[^&#]*", "i");
if (re.test(uri)) {
uri = uri.replace(re, '$1' + paramKey + "=" + paramVal);
} else {
var separator = /\?/.test(uri) ? "&" : "?";
uri = uri + separator + paramKey + "=" + paramVal;
}
return uri;
}
jQuery(document).ready(function($) {
$('#paramKey,#paramValue').on('change', function() {
if ($('#paramKey').val() != "" && $('#paramValue').val() != "") {
$('#uri').val(addOrUpdateUrlParam($('#uri').val(), $('#paramKey').val(), $('#paramValue').val()));
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input style="width:100%" type="text" id="uri" value="http://www.example.com/text.php">
<label style="display:block;">paramKey
<input type="text" id="paramKey">
</label>
<label style="display:block;">paramValue
<input type="text" id="paramValue">
</label>
NOTE This is a modified version of #elreimundo
It's so simple with URLSearchParams, supported in all modern browsers (caniuse).
let p = new URLSearchParams();
p.set("foo", "bar");
p.set("name", "Jack & Jill?");
console.log("http://example.com/?" + p.toString());
If you want to modify the existing URL, construct the object like this: new URLSearchParams(window.location.search) and assign the string to window.location.search.
My take from here (compatible with "use strict"; does not really use jQuery):
function decodeURIParams(query) {
if (query == null)
query = window.location.search;
if (query[0] == '?')
query = query.substring(1);
var params = query.split('&');
var result = {};
for (var i = 0; i < params.length; i++) {
var param = params[i];
var pos = param.indexOf('=');
if (pos >= 0) {
var key = decodeURIComponent(param.substring(0, pos));
var val = decodeURIComponent(param.substring(pos + 1));
result[key] = val;
} else {
var key = decodeURIComponent(param);
result[key] = true;
}
}
return result;
}
function encodeURIParams(params, addQuestionMark) {
var pairs = [];
for (var key in params) if (params.hasOwnProperty(key)) {
var value = params[key];
if (value != null) /* matches null and undefined */ {
pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value))
}
}
if (pairs.length == 0)
return '';
return (addQuestionMark ? '?' : '') + pairs.join('&');
}
//// alternative to $.extend if not using jQuery:
// function mergeObjects(destination, source) {
// for (var key in source) if (source.hasOwnProperty(key)) {
// destination[key] = source[key];
// }
// return destination;
// }
function navigateWithURIParams(newParams) {
window.location.search = encodeURIParams($.extend(decodeURIParams(), newParams), true);
}
Example usage:
// add/update parameters
navigateWithURIParams({ foo: 'bar', boz: 42 });
// remove parameter
navigateWithURIParams({ foo: null });
// submit the given form by adding/replacing URI parameters (with jQuery)
$('.filter-form').submit(function(e) {
e.preventDefault();
navigateWithURIParams(decodeURIParams($(this).serialize()));
});
Based on the answer #ellemayo gave, I came up with the following solution that allows for disabling of the hash tag if desired:
function updateQueryString(key, value, options) {
if (!options) options = {};
var url = options.url || location.href;
var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"), hash;
hash = url.split('#');
url = hash[0];
if (re.test(url)) {
if (typeof value !== 'undefined' && value !== null) {
url = url.replace(re, '$1' + key + "=" + value + '$2$3');
} else {
url = url.replace(re, '$1$3').replace(/(&|\?)$/, '');
}
} else if (typeof value !== 'undefined' && value !== null) {
var separator = url.indexOf('?') !== -1 ? '&' : '?';
url = url + separator + key + '=' + value;
}
if ((typeof options.hash === 'undefined' || options.hash) &&
typeof hash[1] !== 'undefined' && hash[1] !== null)
url += '#' + hash[1];
return url;
}
Call it like this:
updateQueryString('foo', 'bar', {
url: 'http://my.example.com#hash',
hash: false
});
Results in:
http://my.example.com?foo=bar
Here is a shorter version that takes care of
query with or without a given parameter
query with multiple parameter values
query containing hash
Code:
var setQueryParameter = function(uri, key, value) {
var re = new RegExp("([?&])("+ key + "=)[^&#]*", "g");
if (uri.match(re))
return uri.replace(re, '$1$2' + value);
// need to add parameter to URI
var paramString = (uri.indexOf('?') < 0 ? "?" : "&") + key + "=" + value;
var hashIndex = uri.indexOf('#');
if (hashIndex < 0)
return uri + paramString;
else
return uri.substring(0, hashIndex) + paramString + uri.substring(hashIndex);
}
The regex description can be found here.
NOTE: This solution is based on #amateur answer, but with many improvements.
Code that appends a list of parameters to an existing url using ES6 and jQuery:
class UrlBuilder {
static appendParametersToUrl(baseUrl, listOfParams) {
if (jQuery.isEmptyObject(listOfParams)) {
return baseUrl;
}
const newParams = jQuery.param(listOfParams);
let partsWithHash = baseUrl.split('#');
let partsWithParams = partsWithHash[0].split('?');
let previousParams = '?' + ((partsWithParams.length === 2) ? partsWithParams[1] + '&' : '');
let previousHash = (partsWithHash.length === 2) ? '#' + partsWithHash[1] : '';
return partsWithParams[0] + previousParams + newParams + previousHash;
}
}
Where listOfParams is like
const listOfParams = {
'name_1': 'value_1',
'name_2': 'value_2',
'name_N': 'value_N',
};
Example of Usage:
UrlBuilder.appendParametersToUrl(urlBase, listOfParams);
Fast tests:
url = 'http://hello.world';
console.log('=> ', UrlParameters.appendParametersToUrl(url, null));
// Output: http://hello.world
url = 'http://hello.world#h1';
console.log('=> ', UrlParameters.appendParametersToUrl(url, null));
// Output: http://hello.world#h1
url = 'http://hello.world';
params = {'p1': 'v1', 'p2': 'v2'};
console.log('=> ', UrlParameters.appendParametersToUrl(url, params));
// Output: http://hello.world?p1=v1&p2=v2
url = 'http://hello.world?p0=v0';
params = {'p1': 'v1', 'p2': 'v2'};
console.log('=> ', UrlParameters.appendParametersToUrl(url, params));
// Output: http://hello.world?p0=v0&p1=v1&p2=v2
url = 'http://hello.world#h1';
params = {'p1': 'v1', 'p2': 'v2'};
console.log('=> ', UrlParameters.appendParametersToUrl(url, params));
// Output: http://hello.world?p1=v1&p2=v2#h1
url = 'http://hello.world?p0=v0#h1';
params = {'p1': 'v1', 'p2': 'v2'};
console.log('=> ', UrlParameters.appendParametersToUrl(url, params));
// Output: http://hello.world?p0=v0&p1=v1&p2=v2#h1
To give an code example for modifying window.location.search as suggested by Gal and tradyblix:
var qs = window.location.search || "?";
var param = key + "=" + value; // remember to URI encode your parameters
if (qs.length > 1) {
// more than just the question mark, so append with ampersand
qs = qs + "&";
}
qs = qs + param;
window.location.search = qs;
A different approach without using regular expressions. Supports 'hash' anchors at the end of the url as well as multiple question mark charcters (?). Should be slightly faster than the regular expression approach.
function setUrlParameter(url, key, value) {
var parts = url.split("#", 2), anchor = parts.length > 1 ? "#" + parts[1] : '';
var query = (url = parts[0]).split("?", 2);
if (query.length === 1)
return url + "?" + key + "=" + value + anchor;
for (var params = query[query.length - 1].split("&"), i = 0; i < params.length; i++)
if (params[i].toLowerCase().startsWith(key.toLowerCase() + "="))
return params[i] = key + "=" + value, query[query.length - 1] = params.join("&"), query.join("?") + anchor;
return url + "&" + key + "=" + value + anchor
}
Use this function to add, remove and modify query string parameter from URL based on jquery
/**
#param String url
#param object param {key: value} query parameter
*/
function modifyURLQuery(url, param){
var value = {};
var query = String(url).split('?');
if (query[1]) {
var part = query[1].split('&');
for (i = 0; i < part.length; i++) {
var data = part[i].split('=');
if (data[0] && data[1]) {
value[data[0]] = data[1];
}
}
}
value = $.extend(value, param);
// Remove empty value
for (i in value){
if(!value[i]){
delete value[i];
}
}
// Return url with modified parameter
if(value){
return query[0] + '?' + $.param(value);
} else {
return query[0];
}
}
Add new and modify existing parameter to url
var new_url = modifyURLQuery("http://google.com?foo=34", {foo: 50, bar: 45});
// Result: http://google.com?foo=50&bar=45
Remove existing
var new_url = modifyURLQuery("http://google.com?foo=50&bar=45", {bar: null});
// Result: http://google.com?foo=50
Here's my slightly different approach to this, written as an excercise
function addOrChangeParameters( url, params )
{
let splitParams = {};
let splitPath = (/(.*)[?](.*)/).exec(url);
if ( splitPath && splitPath[2] )
splitPath[2].split("&").forEach( k => { let d = k.split("="); splitParams[d[0]] = d[1]; } );
let newParams = Object.assign( splitParams, params );
let finalParams = Object.keys(newParams).map( (a) => a+"="+newParams[a] ).join("&");
return splitPath ? (splitPath[1] + "?" + finalParams) : (url + "?" + finalParams);
}
usage:
const url = "http://testing.com/path?empty&value1=test&id=3";
addOrChangeParameters( url, {value1:1, empty:"empty", new:0} )
"http://testing.com/path?empty=empty&value1=1&id=3&new=0"
This answer is just a small tweak of ellemayo's answer. It will automatically update the URL instead of just returning the updated string.
function _updateQueryString(key, value, url) {
if (!url) url = window.location.href;
let updated = ''
var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"),
hash;
if (re.test(url)) {
if (typeof value !== 'undefined' && value !== null) {
updated = 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];
}
updated = 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];
}
updated = url;
}
else {
updated = url;
}
}
window.history.replaceState({ path: updated }, '', updated);
}
Java script code to find a specific query string and replace its value *
('input.letter').click(function () {
//0- prepare values
var qsTargeted = 'letter=' + this.value; //"letter=A";
var windowUrl = '';
var qskey = qsTargeted.split('=')[0];
var qsvalue = qsTargeted.split('=')[1];
//1- get row url
var originalURL = window.location.href;
//2- get query string part, and url
if (originalURL.split('?').length > 1) //qs is exists
{
windowUrl = originalURL.split('?')[0];
var qs = originalURL.split('?')[1];
//3- get list of query strings
var qsArray = qs.split('&');
var flag = false;
//4- try to find query string key
for (var i = 0; i < qsArray.length; i++) {
if (qsArray[i].split('=').length > 0) {
if (qskey == qsArray[i].split('=')[0]) {
//exists key
qsArray[i] = qskey + '=' + qsvalue;
flag = true;
break;
}
}
}
if (!flag)// //5- if exists modify,else add
{
qsArray.push(qsTargeted);
}
var finalQs = qsArray.join('&');
//6- prepare final url
window.location = windowUrl + '?' + finalQs;
}
else {
//6- prepare final url
//add query string
window.location = originalURL + '?' + qsTargeted;
}
})
});
Here's an alternative method using the inbuilt properties of the anchor HTML element:
Handles multi-valued parameters.
No risk of modifying the # fragment, or anything other than the query string itself.
May be a little easier to read? But it is longer.
var a = document.createElement('a'),
getHrefWithUpdatedQueryString = function(param, value) {
return updatedQueryString(window.location.href, param, value);
},
updatedQueryString = function(url, param, value) {
/*
A function which modifies the query string
by setting one parameter to a single value.
Any other instances of parameter will be removed/replaced.
*/
var fragment = encodeURIComponent(param) +
'=' + encodeURIComponent(value);
a.href = url;
if (a.search.length === 0) {
a.search = '?' + fragment;
} else {
var didReplace = false,
// Remove leading '?'
parts = a.search.substring(1)
// Break into pieces
.split('&'),
reassemble = [],
len = parts.length;
for (var i = 0; i < len; i++) {
var pieces = parts[i].split('=');
if (pieces[0] === param) {
if (!didReplace) {
reassemble.push('&' + fragment);
didReplace = true;
}
} else {
reassemble.push(parts[i]);
}
}
if (!didReplace) {
reassemble.push('&' + fragment);
}
a.search = reassemble.join('&');
}
return a.href;
};
if you want to set multiple parameters at once:
function updateQueryStringParameters(uri, params) {
for(key in params){
var value = params[key],
re = new RegExp("([?&])" + key + "=.*?(&|$)", "i"),
separator = uri.indexOf('?') !== -1 ? "&" : "?";
if (uri.match(re)) {
uri = uri.replace(re, '$1' + key + "=" + value + '$2');
}
else {
uri = uri + separator + key + "=" + value;
}
}
return uri;
}
same function as #amateur's
if jslint gives you an error add this after the for loop
if(params.hasOwnProperty(key))
There are a lot of awkward and unnecessarily complicated answers on this page. The highest rated one, #amateur's, is quite good, although it has a bit of unnecessary fluff in the RegExp. Here is a slightly more optimal solution with cleaner RegExp and a cleaner replace call:
function updateQueryStringParamsNoHash(uri, key, value) {
var re = new RegExp("([?&])" + key + "=[^&]*", "i");
return re.test(uri)
? uri.replace(re, '$1' + key + "=" + value)
: uri + separator + key + "=" + value
;
}
As an added bonus, if uri is not a string, you won't get errors for trying to call match or replace on something that may not implement those methods.
And if you want to handle the case of a hash (and you've already done a check for properly formatted HTML), you can leverage the existing function instead of writing a new function containing the same logic:
function updateQueryStringParams(url, key, value) {
var splitURL = url.split('#');
var hash = splitURL[1];
var uri = updateQueryStringParamsNoHash(splitURL[0]);
return hash == null ? uri : uri + '#' + hash;
}
Or you can make some slight changes to #Adam's otherwise excellent answer:
function updateQueryStringParameter(uri, key, value) {
var re = new RegExp("([?&])" + key + "=[^&#]*", "i");
if (re.test(uri)) {
return uri.replace(re, '$1' + key + "=" + value);
} else {
var matchData = uri.match(/^([^#]*)(#.*)?$/);
var separator = /\?/.test(uri) ? "&" : "?";
return matchData[0] + separator + key + "=" + value + (matchData[1] || '');
}
}
This should serve the purpose:
function updateQueryString(url, key, value) {
var arr = url.split("#");
var url = arr[0];
var fragmentId = arr[1];
var updatedQS = "";
if (url.indexOf("?") == -1) {
updatedQS = encodeURIComponent(key) + "=" + encodeURIComponent(value);
}
else {
updatedQS = addOrModifyQS(url.substring(url.indexOf("?") + 1), key, value);
}
url = url.substring(0, url.indexOf("?")) + "?" + updatedQS;
if (typeof fragmentId !== 'undefined') {
url = url + "#" + fragmentId;
}
return url;
}
function addOrModifyQS(queryStrings, key, value) {
var oldQueryStrings = queryStrings.split("&");
var newQueryStrings = new Array();
var isNewKey = true;
for (var i in oldQueryStrings) {
var currItem = oldQueryStrings[i];
var searchKey = key + "=";
if (currItem.indexOf(searchKey) != -1) {
currItem = encodeURIComponent(key) + "=" + encodeURIComponent(value);
isNewKey = false;
}
newQueryStrings.push(currItem);
}
if (isNewKey) {
newQueryStrings.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
}
return newQueryStrings.join("&");
}
By using jQuery we can do like below
var query_object = $.query_string;
query_object["KEY"] = "VALUE";
var new_url = window.location.pathname + '?'+$.param(query_object)
In variable new_url we will have new query parameters.
Reference: http://api.jquery.com/jquery.param/
I wanted something that:
Uses the browser's native URL API
Can add, update, get, or delete
Expects the query string after the hash e.g. for single page applications
function queryParam(options = {}) {
var defaults = {
method: 'set',
url: window.location.href,
key: undefined,
value: undefined,
}
for (var prop in defaults) {
options[prop] = typeof options[prop] !== 'undefined' ? options[prop] : defaults[prop]
}
const existing = (options.url.lastIndexOf('?') > options.url.lastIndexOf('#')) ? options.url.substr(options.url.lastIndexOf('?') + 1) : ''
const query = new URLSearchParams(existing)
if (options.method === 'set') {
query.set(options.key, options.value)
return `${options.url.replace(`?${existing}`, '')}?${query.toString()}`
} else if (options.method === 'get') {
const val = query.get(options.key)
let result = val === null ? val : val.toString()
return result
} else if (options.method === 'delete') {
query.delete(options.key)
let result = `${options.url.replace(`?${existing}`, '')}?${query.toString()}`
const lastChar = result.charAt(result.length - 1)
if (lastChar === '?') {
result = `${options.url.replace(`?${existing}`, '')}`
}
return result
}
}
// Usage:
let url = 'https://example.com/sandbox/#page/'
url = queryParam({
url,
method: 'set',
key: 'my-first-param',
value: 'me'
})
console.log(url)
url = queryParam({
url,
method: 'set',
key: 'my-second-param',
value: 'you'
})
console.log(url)
url = queryParam({
url,
method: 'set',
key: 'my-second-param',
value: 'whomever'
})
console.log(url)
url = queryParam({
url,
method: 'delete',
key: 'my-first-param'
})
console.log(url)
const mySecondParam = queryParam({
url,
method: 'get',
key: 'my-second-param',
})
console.log(mySecondParam)
url = queryParam({
url,
method: 'delete',
key: 'my-second-param'
})
console.log(url)
Yeah I had an issue where my querystring would overflow and duplicate, but this was due to my own sluggishness. so I played a bit and worked up some js jquery(actualy sizzle) and C# magick.
So i just realized that after the server has done with the passed values, the values doesn't matter anymore, there is no reuse, if the client wanted to do the same thing evidently it will always be a new request, even if its the same parameters being passed. And thats all clientside, so some caching/cookies etc could be cool in that regards.
JS:
$(document).ready(function () {
$('#ser').click(function () {
SerializeIT();
});
function SerializeIT() {
var baseUrl = "";
baseUrl = getBaseUrlFromBrowserUrl(window.location.toString());
var myQueryString = "";
funkyMethodChangingStuff(); //whatever else before serializing and creating the querystring
myQueryString = $('#fr2').serialize();
window.location.replace(baseUrl + "?" + myQueryString);
}
function getBaseUrlFromBrowserUrl(szurl) {
return szurl.split("?")[0];
}
function funkyMethodChangingStuff(){
//do stuff to whatever is in fr2
}
});
HTML:
<div id="fr2">
<input type="text" name="qURL" value="http://somewhere.com" />
<input type="text" name="qSPart" value="someSearchPattern" />
</div>
<button id="ser">Serialize! and go play with the server.</button>
C#:
using System.Web;
using System.Text;
using System.Collections.Specialized;
public partial class SomeCoolWebApp : System.Web.UI.Page
{
string weburl = string.Empty;
string partName = string.Empty;
protected void Page_Load(object sender, EventArgs e)
{
string loadurl = HttpContext.Current.Request.RawUrl;
string querySZ = null;
int isQuery = loadurl.IndexOf('?');
if (isQuery == -1) {
//If There Was no Query
}
else if (isQuery >= 1) {
querySZ = (isQuery < loadurl.Length - 1) ? loadurl.Substring(isQuery + 1) : string.Empty;
string[] getSingleQuery = querySZ.Split('?');
querySZ = getSingleQuery[0];
NameValueCollection qs = null;
qs = HttpUtility.ParseQueryString(querySZ);
weburl = qs["qURL"];
partName = qs["qSPart"];
//call some great method thisPageRocks(weburl,partName); or whatever.
}
}
}
Okay criticism is welcome (this was a nightly concoction so feel free to note adjustments). If this helped at all, thumb it up, Happy Coding.
No duplicates, each request as unique as you modified it, and due to how this is structured,easy to add more queries dynamicaly from wthin the dom.