Ask Ben: Getting Query String Values In JavaScript
I got a CGI/Perl script that reads databases and displays the results on template pages. I need to be able to get the name of the database that is being read from the url location to be able to use it in a link in the template page. The URL would look something like this:
perl.cgi?i=database&w=template&re=date&rada=012
in which I would like to get the [i] field from the URL. I heard an idea about creating a dummy <span> tag with an id, then within the span tag use document.write to write the location url into the span tag, then using the getElementById it might be possible to use substr(value1,value2) to identify the database call in the url. I just don't know how to do this if it works. Any ideas about what I could do to solve this problem?
There are many ways to get the query string values (name-value pairs) out of the URL (location) of the current page. Many examples use the String::split() method to split the location on the "?", the "&", and the "=". This keeps breaking the URL up into smaller arrays that can eventually be used to grab the query string name-value pairs. This is a decent solution, and may in fact be faster than my solution, but I find my own solution a bit more simple.
I use the window.location.search value to return the query string itself from the url. This is one of those cases where if you don't know it exists, you can't use it. You will see a lot of examples out there that split the URL on the "?" in attempt to separate the query string from the rest of the URL. This is entirely unnecessary as Javascript already provides a reference directly to the query string via window.location.search.
Once I have the search value, I harness the power of the String::replace() method to match the name-value pairs as regular expressions. Javascripts String::replace() is such a bad-ass method and will, in fact, iterate over each matched pattern. We can then send that pattern to a headless function which will keep track of the name-value pairs.
Once we have all these name-value query string pairs, we can either write them to the document as the document is still loading or, we can write them to DOM-accessed nodes after the document has finished loading:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Parsing URL Query String In Javascript</title>
<script type="text/javascript">
// Build an empty URL structure in which we will store
// the individual query values by key.
var objURL = new Object();
// Use the String::replace method to iterate over each
// name-value pair in the query string. Location.search
// gives us the query string (if it exists).
window.location.search.replace(
new RegExp( "([^?=&]+)(=([^&]*))?", "g" ),
// For each matched query string pair, add that
// pair to the URL struct using the pre-equals
// value as the key.
function( $0, $1, $2, $3 ){
objURL[ $1 ] = $3;
}
);
</script>
</head>
<body>
<p>
<script type="text/javascript">
// Loop over the URL values that we collected.
for (var strKey in objURL){
// Output the key to the document.
document.write(
"<strong>" + strKey + ":</strong> "
);
// Output the value stored at that key.
document.write(
objURL[ strKey ] + "<br />"
);
}
</script>
</p>
<p>
<strong>Database:</strong>
<span id="database"></span>
</p>
<script type="text/javascript">
// Now that the most of the document is loaded, we
// can reach into the DOM and set values on specific
// node elements. Let's set the Database node.
var objNode = document.getElementById( "database" );
// Check to make sure we have a valid node and that
// our URL does indeed contain the proper variable.
if (objNode && objURL[ "i" ]){
// Set the inner HTML to reflect the value of
// the database that was sent in the URL.
objNode.innerHTML = objURL[ "i" ];
}
</script>
</body>
</html>
When we run the HTML above at the given URL:
page.cfm?i=database&w=template&re=date&rada=012
... this is the output of the resultant page:
i: database
w: template
re: date
rada: 012
Database: database
Notice that the first 4 values were written as the document was loading (using document.write()). Then, once the document finished loading (just about), we grabbed a DOM node reference based on the ID, "database," and wrote the URL value to its innerHTML. These are two valid was of handling this situation.
Just keep in mind that, unlike ColdFusion, Javascript is very much case sensitive. That means that the URL values that are parsed are kept in case; therefore, the objURL will have a value for "i" but will NOT have a value for "I". Also, I parsed the entire query string value in one go. This is not required. You could create a function that takes a name and returns the query string value for that name. This is fine to do, but if you are going to get more than one value, I would recommend just parsing the entire query string in one go to get the processing over and done with.
Want to use code from this post? Check out the license.
Reader Comments
A split-based method (which seems more correct since you're not replacing anything) doesn't need to be much more complicated. E.g.:
// Parse the query (supports keys not followed by "=", and values which contain "=")
var q = (location.search.length > 1 ? location.search.substring(1).split("&") : []);
var qKeys = {};
for(var i = 0; i < q.length; i++){
qKeys[q[i].match(/^[^=]+/)] = q[i].replace(/^[^=]+=?/, "");
}
-----
You could also use an exec()-based approach, which would probably be even better/easier, come to think of it:
var qKeys = {};
var re = /[?&]([^=]+)(?:=([^&]*))?/g;
var matchInfo;
while(matchInfo = re.exec(location.search)){
qKeys[matchInfo[1]] = matchInfo[2];
}
@Steve,
I am not familiar with the exec() method. From a very cursory Google search, it seems to globally update some regular expression property about the matches in the current string. I will have to look into this further. Thanks for the advices.
The RegExp exec method returns all backreferences from a match in an array. If no matches are found it returns null. It also stores a couple properties on the returned array (the input and the index of the match), and on the regex object itself (the index where the match ended [lastIndex], as well as the source regex and Boolean values for each regex modifier, indicating if they were used).
The trick here is that if you use the exec method on a regex with the /g (global) modifier, it starts the match from the value of lastIndex. So, each time through the while loop, it's picking up where it left off on the last match, until there are no more matches and it returns null (ending the loop).
@Steve,
That sounds cool. I like the fact that it picks up where it left off, sounds nice an efficient. This will be fun to play around with.
@Steve,
This stuff is really cool. I did some playing around, and I think I am really gonna like this methodology. Thanks for bringing it to my attention.
www.bennadel.com/index.cfm?dax=blog:697.view
For some reason, despite it being extremely useful, very few people seem to know about it or how to use it well. Even the Mozilla documentation for the method seems very neglected, with one example of "using exec" not even using exec (!), and another example doing a comparison against undefined to end a similar while loop, even though exec actually returns null (hence, their example code only works due to type conversion... i.e., (undefined == null) is true, but (undefined === null) is false).
Here's the page I'm talking about:
http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:RegExp:exec
(Before anyone brings it up, of course my example uses type conversion as well [relying on null being falsie], but at least it avoids being misleading, hopefully.)
Ha ha ha, yeah, that's where I looked up some of this stuff. I noticed that last example on the page... It has nothing to do with exec() :) I even did a page find to see if maybe I was just not seeing it. Sure enough, it just looped back up the top of the page.
Gentlemen,
It seems to me the final example does call exec():
var firstName = /\w+/i(input.value);
While it bends my C++ mind, that looks like a pretty straight call to me as given by the first paragraph of the exec() Description.
firstName is an array where firstName[0] is the result for each iteration.
The comparison with undefined is, I agree, strange and clearly untested in the earlier example. I hope that's not how Firefox is tested.
Kevin
p.s. the combination of discovering window.location.search instead of document.location.search along with the query string parse and RegExp discussion makes this the most valuable information per word discovery I've made this year.
Kevin, you are certainly correct (regarding Mozilla's exec documentation, that is). Thanks for correcting my error.
@Kevin,
Reading regular expressions with that funky /.../ notation confuses me so I tend to just gloss over it. Now, I'm just an unfrozen ColdFusion programmer thawed out by your scientists; my primitive mind does not understand crazy notation. But what I do know is that I like seeing new RegExp().
Awesome script! I was able to use your code to pull in a URL that had a variable (mysite.htm?myVariable=value) & pass it to a function easily!
(Head script)
var urlObj = new Object();
window.location.search.replace(
new RegExp( "([^?=&]+)(=([^&]*))?", "g" ),
function( $0, $1, $2, $3 ){
urlObj[ $1 ] = $3;
}
);
(in the function that needed the variable)
for (var strKey in urlObj){
var myVariable = urlObj[ strKey ];}
Was tired of using convoluted substring queries and splits.... Worked like a charm... THANKS MAN!!!! :-)
-Tom
@Tom,
Always glad to help :)
Hi,
I modified the re to support parameters without values (flags) like
?par1=v1&flagX&par2=v2
/[?&]([^=&]*)=?([^&]*)/g
cheers
I love this script - but I'm having an issue. I want to strip a single variable from the URL and put it in the middle of a tag. For instance, I want to pull variable &abc=123 and parse "123" into an <iframe> tag.
Like:
<iframe src="http://www.url.com/123.html">
Is there a way to do this?
Once you get the variable value, give your IFRAME a name tag and a name, and then just put your parameter into a string like:
var url = "http://www.url.com/" & theVariableRepresentingTheUrlParameter & ".htm";
IFRAMEname.src = url;
Put that into its own function with the URL parser, then use an onlick or onload or whatever event you are looking for to change your IFRAME's src to that URL.
Hope that helps.
Tom
Sorry - in my last comment those "&" should have been "+" since it's JavaScript. "&" are used in VB.
@Tom,
Thanks for jumping in there.
OK - I guess I don't get it. I have all the script included above - but I'm not seeing how to assemble this...
Probably 'cause I was wrong... use ID, not name, for your IFRAME tag, then reference it like this:
function returnUrlObjVal() {
if (s[0]) {
var iFrameForm = s[0];
var url = "http://www.url.com/" + iFrameForm + ".html"
myFrame.window.location.href = url;
}
}
Don't use src like I said before, either, use window.location.href. "s[0]" is an array variable from reading in the parsed URL.
Happy coding,
Tom
OK - I think this is what we're trying to do here - but it still doesn't seem to work. What am I missing?
<html>
<head>
<title>Untitled Document</title>
</head>
<body>
<script type="text/javascript">
function returnUrlObjVal() {
if (s[0]) {
var iFrameForm = s[0];
var url = "http://www.url.com/" + catID + ".html"
ZZZ.window.location.href = url;
}
}
</script>
<iframe id="ZZZ">
</body>
</html>
You have it all except for how to get the parsed value. That part was the whole point of this website/page, so I figured you had to have figured that part out already, or at the very least I figured you could have figured out how to use Ben's code to do so (and the numerous posts below it). Don't expect everyone to do everything for you (even though if you look hard enough on this site, they have).
I should tell you, though, that the "s[0]" variable I put in my last post is the same thing as if I had used Ben's code to give me objURL[0], above. You need the URL parsing code from Ben's example to give you the value from the URL string - it'll be almost a direct copy-paste, anyway. That key info should help you finish your project.
-Tom
Woahhhhhh there.
I really appreciate your help.
I had the other JS statements, but I'm not clear where to put the code you posted - the more I looked at it, the more I started to second guess myself and thought that I DIDN'T need the other code. I'm just unclear where to put your extra stuff.
I've been working on this for three days, but I'm definetley not an expert coder so this stuff confuses me a bit.
Best thing to do is to start off with Ben's complete code, then put in my function, not the other way around. If you replace my s[0] with objUrl[0], and put in your IFRAME tag into the HTML body, that's all there is to it. Test by putting file:\\\C:\folderMyFileIsIn\mypage.htm?url=test.html on your address bar.
Sorry, I accidentally deleted this post when filtering SPAM:
Here....
[code]
<HTML><HEAD><TITLE>Parsing a URL</TITLE>
<SCRIPT language="JavaScript">
//<![CDATA[
var urlObj = new Object();
window.location.search.replace(
new RegExp( "([^?=&]+)(=([^&]*))?", "g" ),
function( $0, $1, $2, $3 ){
urlObj[ $1 ] = $3;
});
s = new Array();
var i = 0;
for (var strKey in urlObj) {
s[i] = urlObj[ strKey ];
i++;
}
function returnUrlObjVal() {
if (s[0]) {
var iFrameForm = s[0];
var url = "http://www.url.com/" + iFrameForm + ".html";
myFrame.window.location.href = url;
}
}
//]]>
</SCRIPT>
</HEAD><BODY onload="returnUrlObjVal()">
<FORM name="theForm">
<BR><BR>
<IFRAME ID="myFrame" height="500" width="800"></IFRAME>
<BR><BR></FORM>
</BODY></HTML>
[/code]
Really very helpful. Thanks
Excellent article. This is the easiest method i could find for readying query string using JavaScript.
awesome
thanks
/john/
And for those trying to find a specific parameter and get back a value for it (like the Request.Querystring[""] method in ASP), this is a good site: http://www.eggheadcafe.com/articles/20020107.asp.
-Tom
Excelent!!
Thanks a lot!
Jonathan
Buenos Aires, Argentina
Well, they removed the page from that eggheadcafe link, so I'll just provide the code. To use this, call displayItem('ItemYouWantFromTheURL') :
function PageQuery(q) {
if(q.length > 1) this.q = q.substring(1, q.length);
else this.q = null;
this.keyValuePairs = new Array();
if(q) {
for (var i=0; i < this.q.split("&").length; i++) {
this.keyValuePairs[i] = this.q.split("&")[i];
for (var z=0; z < this.keyValuePairs[i].length; z++)
{
if(this.keyValuePairs[i].substring(z,z+1) == "?")
{
this.keyValuePairs[i] = this.keyValuePairs[i].split("?")[0];
}
}
}
for(var i=0; i < this.keyValuePairs[i].split("?").length; i++) {
this.keyValuePairs[i] = this.q.split("&")[i];
}
}
this.getKeyValuePairs = function() { return this.keyValuePairs; }
this.getValue = function(s) {
for(var j=0; j < this.keyValuePairs.length; j++) {
if (this.keyValuePairs[j].split("=")[0] == s) {
return this.keyValuePairs[j].split("=")[1];
}
}
return false;
}
this.getParameters = function() {
var a = new Array(this.getLength());
for(var j=0; j < this.keyValuePairs.length; j++) {
a[j] = this.keyValuePairs[j].split("=")[0];
}
return a;
}
this.getLength = function() { return this.keyValuePairs.length; }
}
function queryString(key){
var page = new PageQuery(window.location.search);
return unescape(page.getValue(key));
}
function displayItem(key){
if(queryString(key)!='false')
{
return queryString(key);
}
}
Nice code.
How could I display the query strings as vars and then carry them from through the users visit to end up at a form and when the user submits the form the vars are then attached to the forms email?
var myOldVariable = displayItem('varInURL');
If you are doing operations on the variable, and want to carry it through to another page when clicking a Submit button, as part of the onclick event for the Submit button, have it called like this:
<input type="button" value="Submit" onclick="myPoster(myOldVariable, myNewValue);" />
and then in the myPoster function, update the URL:
var url = window.location();
var urlPieces = url.split("varInURL=" + myOldVariable);
var newUrl = urlPieces[0] + "varInURL=" + myNewValue + urlPieces[1];
window.location.href = newUrl;
To fully answer your question about sending the terms from a URL in an e-mail, you'd be best served by creating a C# web site in ASP .NET through Visual Studio for e-mailing the results out, once you have a page that will change the "window.location.href" address to the website representing your mail sender page, with all of the right variables and their values in place in that URL. You would just use string myVariable = Request.QueryString["varInURL"] at that point, and then review the System.Mail documentation in the MSDN Library for implementing the code (http://msdn.microsoft.com/en-us/library/system.net.mail.mailmessage.body.aspx). You would make the message.Body equal to the myVariable value. If you have multiple URL variables, concatenate with a plus symbol and add spaces between your variables, like this: myVariable + " " + myOtherVariable.
Hi
I realise this is an old post, but hoping you might be able to help. I'd like to extract all the data in the 'value' variables in the following URL - when I use the code in this post it gets me everything but! :)
http://Intranetpage/netpub/server.np?find=find&catalog=catalog&site=testSite&template=results.np&sorton=fooba+No&field=fooba+No&op=contains&value=2&field=JobTitle&op=contains&value=a&field=JobTitle&op=contains&value=&ascending=0&go=Search
As you can see, there are some entry 'value' values. There could be more that is shown here too.
Any help you can offer would be much appreciated.
Thanks
Steven
I saw in the URL string you have at least 3 "&value=" statements. Each item in your URL needs to be unique. You can't have three of the same variable name in the URL string, or the code will default to the last one (the empty value in the URL string).
Try changing them to different names and see how it goes.
-Tom
And on that eggheadcafe link - it's actually still there, just my period ran into the hyperlink and messed it up. It's: http://www.eggheadcafe.com/articles/20020107.asp .
-Tom
@Steven,
As @Tom mentioned, each value needs to be unique, or the code will overwrite whatever is last. That said, you can update the code to handle this in a few ways.
In a ColdFusion context, duplicate variables get converted into a comma-delimited list. As such:
value=foo&value=bar
... becomes:
value=foo,bar
... on the server. I happen to love this and use it a lot (especially since ColdFusion has fantastic list manipulation). If you want to go that way, you simply have to check to see if a value already exists, and if so, append it with a preceeding comma.
But, since we are in Javascript, which is not as list friendly, you might want to convert it to an array, such that it becomes:
value = [ "foo", "bar" ]
Of course, to do that, you would need to check to see if the existing value is there, and if so, convert the whole value to an array and append.
Really, it comes down to how you want to deal with it. I am not sure that any way is more useful than the other until you give us a use-case.
Ben,
Just found this after many other attempts. Works like a charm! Thanks for sharing.
BTW, what benefits, if any does the exec usage have over your example?
Another question.
This loops through the items after ? in a for loop and lists them in succession.
How would I separate each item that has it's own label?
Instead of showing:
label1ID: test
label2ID: test1
show this:
label1 blah blah: test
new row,
label2 blah blah: test1
@Chris,
There is an "intent" difference. As Steven Levithan pointed out earlier in the comments, I'm not really "replacing" anything; so, it seems a bit odd to use a "replace" method to extract data. That said, I just happen to be more comfortable with the way the replace() method happens to work.
Exec() is really cool, I just haven't gotten into the groove of using it. It breaks out the captured groups into array indexes. Plus, when you use exec(), you get much more insight into where the match is. For more info, check out this link:
www.bennadel.com/blog/697-Javascript-Exec-Method-For-Regular-Expression-Matching.htm
@Chris,
The whole output-via-loop thing was just for demonstration purposes. You probably wouldn't need to do that in an actually application (as you typically need to get URL variables for a business purpose - not simply for display).
Ben,
great solution but I do have a question:
If the url is perl.cgi#i=database&w=template&re=date&rada=012
where the hashtag replaces the question mark how do you modify the regexp to check for the value pairs not just with a question mark preceding them in the url but with a hash tag, where the string values come AFTER the hash tag.
how would you modify:
new RegExp( "([^?=&]+)(=([^&]*))?", "g" )
AND
parser: /(?:^|&|;)([^&=;]*)=?([^&;]*)/g
to allow it to get the values whether there's a question mark or hash tag in the url before the values.
Thanks so much.
Ibuku.
Helpful piece of code. Thanks.
I've got a value coming in which I want to pass onto the next page. However, the escape characters in the query string are complicating matters. For instance:
Initial value: Testing - escape:characters(+)
Value from document.location.search: Testing+-+escape%3Acharacters%28%2B%29
Notice the spaces become pluses and the other special characters are encoded. I've searched for quite a while looking for this answer but haven't found much. Any help is appreciated. Thanks.
@Robert,
You'd need to decode the characters back out. You could write a function to accept your string and replace the encoded characters with the decoded ones:
var myNewString = myOldString.replace('%3A',':');
myNewString = myNewString.replace('+',' ');
myNewString = myNewString.replace('%28','(');
myNewString = myNewString.replace('%2B','+');
myNewString = myNewString.replace('%29',')');
Notice that it will be important to change the pluses to spaces first, before you set the %2B characters to pluses, so you don't end up making those spaces, as well. There's a whole list of these codes at http://www.w3schools.com/tags/ref_urlencode.asp.
My querystring= http://localhost:8080/onlinebookwar/pur.jsp?t=10&z=7
I want to get the values 10 and 7 seperately… where my should be id=10,i=7 does this for loop s correct ..pls check and tel me…
coding:
<head>
<%String chh="";
String s=request.getQueryString();
//here spliting of 2 values takes place
for(int j=2;s.charAt(j)!='&';j++)
{
chh=chh+s.charAt(j);
}
String s1=request.getParameter("chh");
int id=Integer.parseInt(s1);
String s2=request.getParameter("id2");
int i=Integer.parseInt(s2);
%>
nice script!
only one question, how would I delete a particular objURL and strKey without changing the url?
Or possibly skip the document.write() on a particular strKey and objURL
If you know the parameter you want to skip, in the loop, just put an "if" statement to tell it to do whatever you want for the parameters that are not that one. Say the parameter you want to skip is "ID":
var myParam = "ID";
for (var strKey in urlObj) {
if (strKey != myParam) {
s[i] = urlObj[ strKey ];
}
i++;
}
Just remember that "ID" is skipped, so s[0] will be empty. If you don't want to skip it like this, but actually have all the holes filled, just move the "i++;" into the "if" statement but remember the "s" value won't correspond to the order in the URL anymore - it will be in the order they were recorded (i.e. if you have ?ID=1&loc=home, s[0] becomes "home" instead of "1", if you skip "ID").
Thanks, this works,
I had to, however, type the i++ in an else{} statement after the if, this way strKey = "id" (and yes you guessed right, I actually wanted to skip id!) was skipped!
Thanks a million!
I want to strip a single variable from the URL and put it in the middle of a tag. For instance, I want to pull variable &abc=123 and parse "123" into an <iframe> tag.
Thanks for the example code of how to do this, very useful. This works perfectly if for example the variable is only &abc=tv, what if I want to achieve the same result when the variable is presented like this &abc=tv+games or even &abc=games+tv
Essentially if a specific word (in this case tv) appears anywhere within the query I want to display the page http://mysite.com/tv.html within the iFrame.
Secondly, if a query doesn't contain a word that I have setup an Iframe page for how can I display a default Iframe page instead, for example &abc=tv will display the page http:www.mysite.com/tv.html in the iFrame, however if it is &abc=somethingelse then the iFrame returns a Page Not Found - in this case I would like to be able to show instead http://www.mysite.com/default.html within the iFrame.
Any assistance on this would be greatly appreciated.
This is awesome, exactly what I was looking for. Using this in an AngularJS project because I do not use AngularJS routes...using express.js. Angular's $location service does not work great if you do not use their routes and hashbanged URLs.