By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
444,058 Members | 1,209 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 444,058 IT Pros & Developers. It's quick & easy.

URL parameters by name or iteration..

P: n/a
I have written a few scripts to parse the
URL arguments and either list them or allow
access to the value of any parameter by name.
<http://www.physci.org/test/003url/index.html>
<http://www.physci.org/test/003url/index.html?url=http://mybiz.com/&that=this&when=now#21>
<http://www.physci.org/test/003url/index.html?url=http://mybiz.com/&when=now>

Before I go offering it in public (and writing it
into any number of the 'development kits' I deliver
through my site), I would like to get it reviewed
by the people who know Javascript.

Is this code as good as it can be?

Some further errata and comments are listed here..
<http://www.physci.org/test/003url/index.html#errata>
The 3 scripts are linked in the page.
[ ..to save you the trouble of pulling up
the source and making a direct fetch. ;-) ]

All comments welcome.

--
Andrew Thompson
http://www.PhySci.org/ Open-source software suite
http://www.PhySci.org/codes/ Web & IT Help
http://www.1point1C.org/ Science & Technology
Jul 23 '05 #1
Share this Question
Share on Google+
11 Replies


P: n/a
*Andrew Thompson* wrote:
I have written a few scripts to parse the
URL arguments and either list them or allow
access to the value of any parameter by name. .... All comments welcome.


Hi Andrew, I've got something similar
(www.andrewu.co.uk/tools/request/) - it's a little different as it's
trying to be a client-side version of ASP's Request.QueryString
collection. As a result it's got a bit of bloat to it and it could do
with a re-write (I never seem to find the time, but it really needs it).
It supports Count, Key and Item methods so you can find out if keys
exist before using them, it also handles multiple keys and has
enumeration via the 'in' statement (caveat applies).

Trouble is, I kind of wish I hadn't written it because people use it to
create inaccessible/unusable web sites (e.g. site navigation that only
works by decoding the querystring client-side, etc.) The script is in a
compressed format at
http://www.andrewu.co.uk/tools/reque...csjsrequest.js (uncompressed
version upon emailing). There's an interactive demo where you can
compare it against the server-side original -
www.andrewu.co.uk/tools/request/demo/.

Best,
--
Andrew Urquhart
- FAQ: www.jibbering.com/faq/
- Archive: www.google.com/groups?q=comp.lang.javascript
- My reply address is invalid, use: www.andrewu.co.uk/contact/
Jul 23 '05 #2

P: n/a
On Sat, 8 May 2004 23:50:42 +0100, Andrew Urquhart wrote:
*Andrew Thompson* wrote:
I have written a few scripts to parse the
URL arguments and either list them or allow
access to the value of any parameter by name. ...
Hi Andrew, I've got something similar
(www.andrewu.co.uk/tools/request/) - it's a little different

<snip>

(chuckles) I suspected if I looked for an half
hour more I'd have found the (one of the..)
pre-existing solutions.

Yours sounds altogether more comprehensive
than my own little scripts. ;-)
Trouble is, I kind of wish I hadn't written it because people use it to
create inaccessible/unusable web sites


Ahhhh. Yes. You make something beautiful
and wonderful that provides that 'bit extra'
in particular arcane situations* and then it
encounters 'people' who base a navigation
system on it, ..and is never the same again. :-(

* My own interest springs from configuring
Java applets.. I have a site where I can
generate applet tags on the server-side, but
most of my applets can be enhanced using
parameters provided by URL, and many of the
people who use them on _their_ sites have no
PHP, JSP etcetera.

Of course, if my scirpts become in any way
well known, I do not doubt people will do
completely stupid things with it.. (shrugs)

Thanks for your input. :-)

--
Andrew Thompson
http://www.PhySci.org/ Open-source software suite
http://www.PhySci.org/codes/ Web & IT Help
http://www.1point1C.org/ Science & Technology
Jul 23 '05 #3

P: n/a
Andrew Thompson wrote:
I have written a few scripts to parse the
URL arguments and either list them or allow
access to the value of any parameter by name. <snip> Before I go offering it in public (and writing it
into any number of the 'development kits' I deliver
through my site), I would like to get it reviewed
by the people who know Javascript.

Is this code as good as it can be?

<snip>

No, it is pedestrian at best.

Starting with your - QueryParameter - object:-

| /** Provide the name of this URL parameter. */
| function getName() {
| return this.sName;
| }
|
| /** Provide the value of this URL parameter. */
| function getValue() {
| return this.sValue;
| }
|
| /** Check if this is the 'paramName' URL parameter. */
| function isParam( paramName ) {
| return ( this.sName==paramName );
| }
|
| /** Construct an URL parameter object, and attach the functions. */
| function QueryParameter( paramName, paramValue ) {
| this.sName = paramName;
| this.sValue = paramValue;
|
| this.getName = getName;
| this.getValue = getValue;
| this.isParam = isParam;
| }

- you are creating 4 global function to support one object. Generally,
code should have as little impact on the global namespace as possible.
For a globally available object constructor that impact does not need to
be more than just the constructor name. The more items added to the
global namespace the more there is a risk of naming collisions and
resulting unexpected interactions.

This is of most significance when creating generalised components (as
appears to be your intention) because the users of a generalised
component should not be expected to be intimate with its implementation
and so should not need to concern themselves with avoiding naming
collisions. You have chosen to create global functions called "getName",
"getValue" and "isParam", which are nice self-explanatory names to use,
but equally they are names that are likely to be well suited elsewhere.
When the those names are just the properties of a type of object then it
doesn't matter if another type of object has the same property names,
but habitually place them in the global namespace and you are asking for
trouble (and individuals who can only employ other people's components
are going to be the least capable of handling any manifest
consequences).

But javascript constructors to have a prototype which allows objets
created with the constructor to inherit common properties, and also
allows those properties to be define without any impact on the global
namespace. Using the prototype pattern to define your object would
result in:-

function QueryParameter(paramName, paramValue){
this.sName = paramName;
this.sValue = paramValue;
}
QueryParameter.prototype.getName = function(){
return this.sName;
};
QueryParameter.prototype.getValue = function(){
return this.sValue;
};
QueryParameter.prototype.isParam = function(paramName){
return (this.sName==paramName);
};

With which all objects created with the constructor automatically
acquire all of the methods defined on the prototype without any need to
explicitly assign function object to object properties, and without the
creation of the methods needing to have any impact on the global
namespace.

But this namespace problem caries over in to the rest of the system,
which defines another 3 functions and 3 global variables, 2 of which are
only temporarily used and so would seem to be performing roles that
would be better suited to function local variables. The configuration
also uses two inline function calls, neither will work without the
other, and the functions being called are unlikely to be applicable in
other situations (especially as they employ global variables and have no
parameters).

I notice that, on the web page, you say; "I considered adding the
objects to a hash (associative array) as suggested by Grant , but
unfortunately could not then figure how to iterate the elements. For me
exploiting the hashtable-like aspects of javascript objects is the
obvious way to store name/value pairs. Doing so allows named property
references to be handled by native code instead of javascript, and
particularly javascript loops.

You use:-

| /** Provides the value for the parameter named by 'key'. */
| function getQueryString(key) {
| var value = null;
| for (var ii=0; ii<parameters.length; ii++) {
| if ( parameters[ii].isParam(key) ) {
| value = parameters[ii].getValue();
| break;
| }
| }
| return value;
| }

- which loops through (potentially) all of the created objects, and
makes two method calls to determine which value to return, and still
will only return the value corresponding with the first name/value pair
encountered on the query string (a general method should be able to
handle multiple occurrences of the same name in the query string.

Using javascript object to store name/value pairs allows much more
efficient retrieval of named properties. Being able to iterate through
the names used is just a matter of recording the names used in a
retrievable form.

The drawback with using a javascript objects to store name/value pairs
is that they have some properties of their own (constructor, toString)
which need some special consideration when the intention is to create
truly general code. But then creating truly general code is not
necessarily a good idea as all code will actually be used in a specific
context, and downloading code to handle possibilities that will never
arise in the specific situation is wasteful. For example a general query
string interface might go:-

/* This function call creates an interface to the name value pairs
from the query string associated with the URL of the current page
in the browsers. The interface is available as a global variable
called - queryStrings - and has two methods:-

queryStrings.getCountFor(name); - Returns the number of values found
in the query string under the - name - provided as a parameter,
or numeric zero if no elements exist under the name.

queryStrings.getValueFor(name, index); - Returns the value found on
the query string associated with the - name - provided as the
first parameter. The second parameter is optional and represents
an index into an array of the values found for the name. The
index defaults to zero if no value is provided. If no value is
found under the index, or no values exist under the name, then
undefined is returned. Otherwise a string value is returned for
each value provided on the query string associated with the
name, or, if a name was found on the query string with no
associated value this method returns boolean true;

queryStrings.getNames(); - Returns a reference to an Array of all
of the names found on the query string. Any modifications made
to the array will persist and be present in the Array that will
be returned by later calls, but will alter the name value pairs
returned by the other methods.

*/
var queryStrings = (function(out){
var list = {};
var keyList = [];
var global = this;
var unsc = (global.decodeURIComponent)?'decodeURIComponent':
'unescape';
var nameSafe = [' ','']; //first element is the space character
function addItem(name, value){
nameSafe[1] = name;
var sName = nameSafe.join('');
if(list[sName]){
list[sName][out[sName].length] = value;
}else{
list[sName] = [value];
keyList[keyList.length] = name;
}
}
if(typeof location != 'undefined'){
var nvp,ofSet, temp = location.search||location.href||'';
if((ofSet = temp.indexOf('?')) > -1){
temp = temp.split("#")[0];
temp = temp.substring((ofSet+1), temp.length);
var workAr = temp.split('&');
for(var c = workAr.length;c--;){
nvp = workAr[c].split('=');
if(nvp.length > 1){
addItem(global[unsc](nvp[0]),global[unsc](nvp[1]));
}else if(nvp.length == 1){
addItem(global[unsc](nvp[0]), true);
}
}
}
}
return ({
getCountFor:function(name){
nameSafe[1] = name;
var sName = nameSafe.join('');
return ((list[sName] && list[sName].length)|0);
},
getValueFor:function(name, index){
nameSafe[1] = name;
var sName = nameSafe.join('');
if(list[sName]){
return list[sName][(index|0)];
} //else return undefined (by default)
},
getNames:function(){
return keyList;
}
});
})(); //inline function expression call.

- and cover most possibilities in query string use. But in a real
situation it would be possible to avoid names in the query string that
would coincide with javascript object properties, know that name/value
pairs would be distinct, know that names would always have accompanying
values, know which values where expected to be used and avoid names and
values that would be modified in URL encoding.

Knowing these things allows much simpler code to be used, but to still
achieve the desired results. e.g.:-

var queryStrings = (function(out){
if(typeof location != 'undefined'){
var temp = location.search||location.href||'';
var nvp, ofSet;
if((ofSet = temp.indexOf('?')) > -1){
temp = temp.split("#")[0];
temp = temp.substring((ofSet+1), temp.length);
var workAr = temp.split('&');
for(var c = workAr.length;c--;){
nvp = workAr[c].split('=');
if(nvp.length > 1){
out[nvp[0]] = nvp[1];
}
}
}
}
return out;
}({})); //inline function expression call passed an object
// literal - {} - as its parameter (out).
I notice your page includes the line; "It is designed to degrade
'gracefully' by telling the user they require JS for this page.". That
is not a definition of "graceful degradation" that I would recognise (it
is never 'graceful' to be bothering the user with this sort of detail).
Though it is not really the role of such a low-level component to
gracefully degrade itself. That is a task for the code that wishes to
use it; to decide how it is going to respond to not being able to
extract the query sting information that it requires/expects. The
responsibility for the component designer is only to ensure that the
code does not actually error when it fails. and to have well defined
behaviour when that failure occurs, for which it is probably only
necessary, in this context, not to return values when they are requested
by name.

Richard.
Jul 23 '05 #4

P: n/a
On Sun, 9 May 2004 04:17:16 +0100, Richard Cornford wrote:
Is this code as good as it can be?

<snip>

No, it is pedestrian at best.


Thank you, thank you, thank you..

I will have a much closer look at what
you wrote over the next day ..or so. ;-)

From what I read of the first 80 or so
lines it seemed you were rattling the
foundations out of the rather fragile, hackish
code I wrote (I also noticed some important
points about URL's that I missed as well).

After I have made changes based on your
advice, I will report back.

Cheers,
--
Andrew Thompson
http://www.PhySci.org/ Open-source software suite
http://www.PhySci.org/codes/ Web & IT Help
http://www.1point1C.org/ Science & Technology
Jul 23 '05 #5

P: n/a
"Andrew Urquhart" <us**************************@spam.invalid> wrote in message news:<vt*************@newsfe2-gui.server.ntli.net>...
*Andrew Thompson* wrote:
I have written a few scripts to parse the
URL arguments and either list them or allow
access to the value of any parameter by name.

...


var entireURL = location.search;
var url=location.search.split('url=')[1];

I don't speak JavaScript, rather Java. Those two lines look suspcious to me.
What if there is no url=something? Does that [1] not then become an out
of bounds subscript?
Jul 23 '05 #6

P: n/a
Richard Cornford wrote:
<snip>
There were some mistakes and typos in that post, so I may as well
correct them (at least the ones that I have noticed so far).
But javascript constructors to have a prototype which allows objets ^^
But javascript constructors have a prototype ...
<snip> I notice that, on the web page, you say; "I considered adding the
objects to a hash (associative array) as suggested by Grant , but
unfortunately could not then figure how to iterate the elements. <snip> ^
There should have been a closing quote mark at the end of that sentence.

<snip>. /* This function call creates an interface to the name value pairs
from the query string associated with the URL of the current page
in the browsers. The interface is available as a global variable
called - queryStrings - and has two methods:- ^^^
It actually has three methods.

<snip> var queryStrings = (function(out){ ^^^
That formal parameter is left over from an earlier version and is not
needed here

<snip> if(list[sName]){
list[sName][out[sName].length] = value; ^^^
I forgot to change that identifier to - list -:-

list[sName][list[sName].length] = value;

<snip> return out;
}({})); //inline function expression call passed an object

<snip> ^
That final closing parenthesis should follow the closing brace:-

})(); // ...

Richard.
Jul 23 '05 #7

P: n/a
On 8 May 2004 21:14:54 -0700, Roedy Green wrote:
*Andrew Thompson* wrote:
I have written a few scripts to parse the
URL arguments and either list them or allow
access to the value of any parameter by name.
.... var entireURL = location.search;
var url=location.search.split('url=')[1];
(sshhh Roedy!) That was JS that predated
the 'much improved' version that Richard
just _decimated_.

I mean ..do you want to embarass me
or something? ;-)
I don't speak JavaScript, rather Java. Those two lines look suspcious to me.
What if there is no url=something? Does that [1] not then become an out
of bounds subscript?


That _original_ script handled
a) no query / with fragment
b) no query / no fragment
c) query / with fragment
d) query / no fragment..
(..I tested it)

Which, I might add, you could have verified
by testing it for _yourself_, either at your
site, or locally on your file system.

All you have to do is use the URL's and
see how it behaves [..much as I might
point out to a Java noob who asked such
a question, ..tut tut. ;-) ]

--
Andrew Thompson
http://www.PhySci.org/ Open-source software suite
http://www.PhySci.org/codes/ Web & IT Help
http://www.1point1C.org/ Science & Technology
Jul 23 '05 #8

P: n/a
On Sun, 9 May 2004 05:40:31 +0100, Richard Cornford wrote:
Richard Cornford wrote:
<snip>
There were some mistakes and typos in that post,...


Thanks for following it up, I will add those
as errrata after I've properly examined the
original huge post.

But, ..missed quote marks??

I had to reread that section of the post
before I actually noticed. You are
striving for precision! ;-)

--
Andrew Thompson
http://www.PhySci.org/ Open-source software suite
http://www.PhySci.org/codes/ Web & IT Help
http://www.1point1C.org/ Science & Technology
Jul 23 '05 #9

P: n/a
Roedy Green wrote:
<snip>
var entireURL = location.search;
var url=location.search.split('url=')[1];

I don't speak JavaScript, rather Java. Those two lines look
suspcious to me.
From a Java perspective they would. But loose-typing, and its
accompanying automatic type-conversion, allow some possibilities that
are unthinkable in Java. Not that loose typing means you don't have to
think about type, you just have to think differently. (That is, the
programmer has to understand (or find out) what types he/she is dealing
with because the source code is not going to tell them.)

The first line is fine (subject to the absence of any feature detection
to verify that the - location - object exists in the environment (I
don't know of a browser where it doesn't, but no harm in making sure)).
If the - location - object implements a - search - property that
property will hold a string primitive value (no problem assigning a
string to a local variable in a loosely typed language). If the -
location - object does not implement a - search - property (and again I
don't know of any that don't) then the property accessor will result in
an - undefined - value (which is a distinct internal type in
javascript), and again assigning - undefined - to a local variable is
perfectly OK. (A variable or object property may be defined and still
hold the value - undefined -, and that is distinct from a variable that
has not been defined (or a property that does not exist on an object).)
What if there is no url=something? Does that [1] not then
become an out of bounds subscript?


That isn't an error in javascript. The feature of Array(s) that is
distinct from Object(s) is the internal handling of the - length -
property (via the Array's special internal - [[Put]] - method (see ECMA
262 3rd edition section 15.4.5.1)), apart from that, what would appear
to be an Array indexing syntax in Java is in fact a bracket notation
property accessor in javascript.

Bracket notation property accessors are identical in mechanism to dot
notation property accessors, so:-

var entireURL = location.search;

- means the same as:-

var entireURL = location['search'];

- and, with:-

var x = 'search';

var entireURL = location[x];

- with the exception that a dot notation property accessor may only use
legal identifiers between/around the dots, while a bracket notation
accessor may use any character sequence in the string used within the
brackets. You cannot index an Array as - ar.1 - because "1" is not a
legal identifier in javascript (ECMAScript), so indexed access to array
elements must use bracket notation. But in practice the number 1 is
internally type-converted to the string "1" for use with the bracket
notation accessor.

With:-

var ar = new Array;

ar[1] = 20;

- and:-

ar['1'] = 20;

- are specified as being identical in terms of which property of the
array is being assigned to (though Netscape 4 (eroniously) thinks
differently).

So if the array returned from the - split - method of the - String -
object (the string primitive is internally type-converted to a String
object when it appears in an object context (the dot notation property
accessor)) only has one element, an attempt to read an element indexed
as 1 is identical to attempting to read a non-existent property of any
object; it will result in the - undefined - type, and assign that value
to the local variable without erroring.

Whether that second line of code is wise/safe depends on what is done
with the - url - variable afterwards (it should be tested to ensure that
it contains a viable value (which would not normally include -
undefined -) prior to any attempt to use it).

Richard.
Jul 23 '05 #10

P: n/a
On Sun, 9 May 2004 07:24:38 +0100, Richard Cornford wrote:
.....
Whether that second line of code is wise/safe depends on what is done
with the - url - variable afterwards (it should be tested to ensure that
it contains a viable value (which would not normally include -
undefined -) prior to any attempt to use it).


I tested it later, if it was defined, I
wrote the appropriate (applet) tag, if
not, nothing was written..

Mind you, there were a number of things
that _could_ still go wrong with the original
code I wrote (it would fail if there were two
parameters, and the url was listed first,
for instance).

--
Andrew Thompson
http://www.PhySci.org/ Open-source software suite
http://www.PhySci.org/codes/ Web & IT Help
http://www.1point1C.org/ Science & Technology
Jul 23 '05 #11

P: n/a
Andrew Thompson <Se********@www.invalid> wrote in message news:<du*****************************@40tude.net>. ..
I have written a few scripts to parse the
URL arguments and either list them or allow


By coincidence, I've been playing with this over the weekend. The
first version was a string method that reads a query string in its
usual &,= form and returns an object. Although it always assumes that
a digit string is a number - and converts it - it seems to be fine for
any uses I can think of right now.

It did strike me that JavaScript Object Notation is suited for this
purpose. The benefit being that a reasonably simple object (one that
only has string, number, boolean or function members) could be
serialised and passed in the URL. I've been using an extention of the
Object::toString method to do this so as to avoid any effect on
enumeration.

Any takers ?
Jul 23 '05 #12

This discussion thread is closed

Replies have been disabled for this discussion.