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

extending a select list

P: n/a
Hi,

The function I've put together below is a rough idea to extend a SELECT
list, starting from:

<body>
<form name="bambam">
<select id="fred">
<option value="1">1</option>
<option value="2">2</option>
</select>

<a onclick="DOMextendSelect('fred',1000);">extend</a>
</form>
</body>
....and although it works I'm not convinced I'm doing it correctly. The
intention is to copy the existing list, extend the copy, then replace
the original with the copy.

Any help would be appreciated.

Ian
function DOMextendSelect(id,extent) {

var old = document.getElementById(id);
var replacement = old;

// to get the maximum value;
var maxV = Number(replacement.options[replacement.options.length-1].value);

var ok = (maxV < extent);

switch(ok) {

case true:

var newOption;
var newOptionText;
var temp;
while(maxV < extent) {
maxV ++;
temp = String(maxV);
newOption = document.createElement("option");
newOptionText = document.createTextNode(temp);
newOption.value = temp;
replacement.appendChild(newOption.appendChild(newO ptionText).parentNode);
}
old.replaceChild(replacement.firstChild,old.firstC hild);
}
Jul 3 '06 #1
Share this Question
Share on Google+
4 Replies


P: n/a
Ian Richardson wrote:
Hi,

The function I've put together below is a rough idea to extend a SELECT
list, starting from:
You mean add options to a select element.

<body>
<form name="bambam">
<select id="fred">
<option value="1">1</option>
<option value="2">2</option>
</select>

<a onclick="DOMextendSelect('fred',1000);">extend</a>
</form>
</body>
...and although it works I'm not convinced I'm doing it correctly. The
intention is to copy the existing list, extend the copy, then replace
the original with the copy.
Why not just add new options?
function DOMextendSelect(id,extent) {

var old = document.getElementById(id);
var replacement = old;

// to get the maximum value;
var maxV = Number(replacement.options[replacement.options.length-1].value);
There is no need to create a number object, the length property is a
number already. Even if it wasn't, the subtraction operation will
convert it to a number if possible:

var maxV = replacement.options.length - 1;

You don't need to do that, nor do you want to subtract one from the
length. You want the number of options equal to extent, don't you?.

var ok = (maxV < extent);

switch(ok) {

case true:
What? why not:

if (! maxV < extent) return;
But that logic can be encapsulated in a while loop, see below...

var newOption;
var newOptionText;
var temp;
while(maxV < extent) {
maxV ++;
while(maxV++ < extent){
temp = String(maxV);
There is no need to create a string object here, nor to store it in
temp.

newOption = document.createElement("option");
newOptionText = document.createTextNode(temp);
That will cause problems in at least one well known but buggy browser.
Use the DOM 0's new Option() method, it is better supported:

old.options[old.length] = new Option(maxV);
It's been fully document here many times, search the archives.

newOption.value = temp;
replacement.appendChild(newOption.appendChild(newO ptionText).parentNode);
}
There is no point in copying the existing elements using a very
circuitous method - if it was required at all (and it isn't here), use
cloneNode. You have now added an option to the end of your existing
select element.
old.replaceChild(replacement.firstChild,old.firstC hild);
The new option is *not* the first child, it is the last one. The above
will simply replace the firstChild with itself - old.firstChild and
replacement.firstChild are the same element, since old and replacement
reference the same object.

You've already added the new option to the element - well, you would
have if not for the bugs in the script.
}
There seems little point in replacing an element with a 'copy' of
itself as a child of exactly the same DOM object it is already a child
of.

var replacement = old;
replacement and old already reference the same object, the above does
nothing of use. If they referenced different objects, then might want
to do:

old.parentNode.replaceChild(replacement, old);

But again, you're replacing an element with itself. I think what you
are trying to do is:
function DOMextendSelect(id,extent)
{
if (!document.getElementById) return;

var sel = document.getElementById(id);
var i = sel.length;
while (i < extent){
sel.options[i] = new Option('text ' + i, 'value ' + i);
++i;
}
}
--
Rob

Jul 4 '06 #2

P: n/a
RobG wrote:
Ian Richardson wrote:
>Hi,

The function I've put together below is a rough idea to extend a SELECT
list, starting from:

You mean add options to a select element.
Fair enough - I was writing this email in a hurry.

<snip>
Why not just add new options?
....because when adding options to a select element using the DOM, Opera
is _blindingly_ fast... The application I'm developing could have
hundreds or thousands of options needing to be added.

>function DOMextendSelect(id,extent) {

var old = document.getElementById(id);
var replacement = old;

// to get the maximum value;
var maxV = Number(replacement.options[replacement.options.length-1].value);

There is no need to create a number object, the length property is a
number already. Even if it wasn't, the subtraction operation will
convert it to a number if possible:
1. The list may not have a first value (and its text), at any
pre-determined value, so it's easier to read the final value.

2. I know Javascript will type convert all over the place. I choose not
to be equally sloppy.
var maxV = replacement.options.length - 1;

You don't need to do that, nor do you want to subtract one from the
length. You want the number of options equal to extent, don't you?.
Not always.

If the list starts at 17 and I was the final entry to be 23...
>var ok = (maxV < extent);

switch(ok) {

case true:

What? why not:

if (! maxV < extent) return;
I could have done without the switch statement, but:

1. I will not have multiple exits from a function.

2. If I'm trying to extend a list which is already at least that long,
the switch statement will skip that code in the fastest way possible,
and only define the other variables and proceed if necessary.

But that logic can be encapsulated in a while loop, see below...
....and it is.

<snip>
>temp = String(maxV);

There is no need to create a string object here, nor to store it in
temp.
There is a perfectly good reason: type conversion. It's slow. Why do it
twice when it can be done once? If you're adding lots of options, why
waste as much time as possible being sloppy?

<snip>
Use the DOM 0's new Option() method, it is better supported:

old.options[old.length] = new Option(maxV);
It's been fully document here many times, search the archives.
I've seen it, I've done it myself. It's in the other half of the code I
didn't include, written for my timing comparisons. It's nice and quick
in Firefox, damnably slow in Internet Exploiter, and Opera can be
measured using a geological clock.

I'm trying top find a faster method for obvious reasons, but I've never
used any of this appendChild, parentNode, etc... stuff before.
>newOption.value = temp;
replacement.appendChild(newOption.appendChild(new OptionText).parentNode);
}

There is no point in copying the existing elements using a very
circuitous method - if it was required at all (and it isn't here), use
cloneNode. You have now added an option to the end of your existing
select element.
>old.replaceChild(replacement.firstChild,old.first Child);

The new option is *not* the first child, it is the last one. The above
will simply replace the firstChild with itself - old.firstChild and
replacement.firstChild are the same element, since old and replacement
reference the same object.
OK, now we've got past the criticism of programming practice and now
down to the core of the issue I'd raised.

I'm now creating the copy of the select list using:

var replacement = old.cloneNode(true);

(which I thought I need in the first place!)

....and the value of the last option in the select list using:

var maxV = Number(replacement.lastChild.value);

....but while the following line works in IE, it fails without error in
Firefox and generates a huge error in Opera 9.

old.replaceNode(replacement,old);

Any further help?

Thanks,

Ian
Jul 4 '06 #3

P: n/a

Ian Richardson wrote:
RobG wrote:
Ian Richardson wrote:
[...]
...because when adding options to a select element using the DOM, Opera
is _blindingly_ fast... The application I'm developing could have
hundreds or thousands of options needing to be added.
That sounds disasterous - thousands of options? Oh well...

function DOMextendSelect(id,extent) {

var old = document.getElementById(id);
var replacement = old;

// to get the maximum value;
var maxV = Number(replacement.options[replacement.options.length-1].value);
There is no need to create a number object, the length property is a
number already. Even if it wasn't, the subtraction operation will
convert it to a number if possible:

1. The list may not have a first value (and its text), at any
pre-determined value, so it's easier to read the final value.
An empty select is invalid HTML, you may have issues on that front -
should a browser render a select that has no options? As far as I
know, they all do. But who's to say they all will, and if they don't
they aren't (strictly) at fault - or are they? What sense will a user
make of a select with no options?

2. I know Javascript will type convert all over the place. I choose not
to be equally sloppy.
How? There is no type conversion, the length property is defined in
the specification as being a number. Converting it from a primitive to
a Number object is simply unnecessary. I suspect that it's slower than
implicit conversion anyway, but in either case the time taken is
infintesimally small and utterly insignificant - it only happens once
per call to the function.

var maxV = replacement.options.length - 1;

You don't need to do that, nor do you want to subtract one from the
length. You want the number of options equal to extent, don't you?.

Not always.

If the list starts at 17 and I was the final entry to be 23...
Then you just want to add options 18 to 23 inclusive - anyhow, I'll
leave that logic to you. I think you've left out too much for me to
know what you are doing here.

var ok = (maxV < extent);

switch(ok) {

case true:
What? why not:

if (! maxV < extent) return;

I could have done without the switch statement, but:

1. I will not have multiple exits from a function.
Then use if - which provides exactly the same functionality as a switch
with only one condition (unless I'm missing something).

if (maxV < extent) {
// add options...
}

2. If I'm trying to extend a list which is already at least that long,
the switch statement will skip that code in the fastest way possible,
and only define the other variables and proceed if necessary.
As will an if, while or do. I think in absolute terms, any of them
will be faster than switch - but we're talking nano seconds here. See
how many loops you need to do with a test harness to get meainingful
timed results.

The general strategy is to write simple, maintainable code avoiding
obvious performance traps. If it then runs slowly, find out where the
slowness is and optimise (if you can) until it's fast enough. Don't
try to optimise what isn't a problem.

[...]
Use the DOM 0's new Option() method, it is better supported:

old.options[old.length] = new Option(maxV);
It's been fully document here many times, search the archives.

I've seen it, I've done it myself. It's in the other half of the code I
didn't include, written for my timing comparisons. It's nice and quick
in Firefox, damnably slow in Internet Exploiter, and Opera can be
measured using a geological clock.

I'm trying top find a faster method for obvious reasons, but I've never
used any of this appendChild, parentNode, etc... stuff before.
Ah, so what you really want is a faster method than new Option()? I'm
not sure there is one specific method that is faster in all browsers.
You can't use createElement in IE, you don't want to use new Option()
because Opera is too slow and if you use innerHTML I'll bet some other
browser drags the chain. Test it and see.

Incidentally, if you decide to do that, don't use the += operator to
concatenate strings in IE, it is very slow (when used thousands of
times in a loop). Better to put the option HTML strings in an array
and use join().

newOption.value = temp;
replacement.appendChild(newOption.appendChild(newO ptionText).parentNode);
}
There is no point in copying the existing elements using a very
circuitous method - if it was required at all (and it isn't here), use
cloneNode. You have now added an option to the end of your existing
select element.
old.replaceChild(replacement.firstChild,old.firstC hild);
The new option is *not* the first child, it is the last one. The above
will simply replace the firstChild with itself - old.firstChild and
replacement.firstChild are the same element, since old and replacement
reference the same object.

OK, now we've got past the criticism of programming practice and now
down to the core of the issue I'd raised.

I'm now creating the copy of the select list using:

var replacement = old.cloneNode(true);

(which I thought I need in the first place!)

...and the value of the last option in the select list using:

var maxV = Number(replacement.lastChild.value);

...but while the following line works in IE, it fails without error in
Firefox and generates a huge error in Opera 9.

old.replaceNode(replacement,old);

Any further help?
If you want to use cloneNode (and I don't know that it will be any
faster cross-browser), then as a general case:

var sel = <a select element reference>;
sel.options.length = 0; // remove all existing options

// can also replace it with a shallow clone of itself - very fast
but will
// break in older Safari at lest:
sel.parentNode.replaceChild(sel.cloneNode(false), sel);

var dummyOpt = new Option(text, value);

while ( <some condition){
sel.options[ i ] = dummyOpt.cloneNode(true);
}
>From memory that doesn't work properly in IE either, please test...
Using innerHTML:

var optHTML = [];
while ( <some condition){

// You don't need closing tags on option elements in HTML 4
optHTML.push('<option value="' + value + '">' + text;
}
sel.innerHTML = optHTML.join('');

Previous testing showed innerHTML to be fastest generally. You might
also try testing for outerHTML, and if supported (probably IE, +
Opera?) use that, if not, use new Option. Gets worse the further you
read, huh? :-)

if (sel.outerHTML) {
// similar to innerHTML loop but using outerHTML
} else {
// new option loop...
}

HTH
--
Rob

Jul 4 '06 #4

P: n/a
RobG wrote:
Ian Richardson wrote:
>RobG wrote:
>>Ian Richardson wrote:
[...]
>...because when adding options to a select element using the DOM, Opera
is _blindingly_ fast... The application I'm developing could have
hundreds or thousands of options needing to be added.

That sounds disasterous - thousands of options? Oh well...
The application allows for setting of the start of an event, either by
an explicit date or an offset. Changing any aspect of one keeps
everything together. If the date is changed such that one of the offset
menus needs to explode in size, the user is warned of this and given the
option to roll back the change if necessary.

However, there are certain date restrictions I perhaps should impose,
like maybe limiting the maximum date to 100 or 150 years in the future.

<snip>
>1. The list may not have a first value (and its text), at any
pre-determined value, so it's easier to read the final value.

An empty select is invalid HTML, you may have issues on that front -
should a browser render a select that has no options? As far as I
know, they all do. But who's to say they all will, and if they don't
they aren't (strictly) at fault - or are they? What sense will a user
make of a select with no options?
1. Did I say at any point that I'd have a select list with no options?
No I didn't.

2. I didn't bother to check previously, but now I know why a select list
with no options has a dummy entry added by some browsers!

>2. I know Javascript will type convert all over the place. I choose not
to be equally sloppy.

How? There is no type conversion, the length property is defined in
the specification as being a number. Converting it from a primitive to
a Number object is simply unnecessary. I suspect that it's slower than
implicit conversion anyway, but in either case the time taken is
infintesimally small and utterly insignificant - it only happens once
per call to the function.
In the example I initially provided, it just so happened that the list
started from 1 and each entry had the text and value one higher than the
one before... so in that special case, the numeric value of the last
option was the same as the numeric length.

The length (i.e. number of options) is a number, yet the values of any
attributes are of type string. To be safe when using this string in any
numeric calculations, I'd convert to a number. I've had these sort of
type-related problems before and I'm not going to change my defensive
coding based on your comments.
>> var maxV = replacement.options.length - 1;

You don't need to do that, nor do you want to subtract one from the
length. You want the number of options equal to extent, don't you?.
Not always.

If the list starts at 17 and I was the final entry to be 23...

Then you just want to add options 18 to 23 inclusive - anyhow, I'll
leave that logic to you. I think you've left out too much for me to
know what you are doing here.
Now you're just giving up. I'll explain for you again:

1. Assumption: the list (both the text and value) are integers.

2. Assumption: the numeric gap between each option in the list is 1.

3. Lists can start at any value.

4. For some reason (see above), there is a requirement to add further
options to the end of the list.

5. Adding options should be done in the fastest manner possible, not
only to reduce the time the user is held up, but also because there may
be a large number of options to add.

6. Adding options in a manner which doesn't halt user interaction causes
problems in the browser when the user attempts to interact with the
select list being extended.
Mind you, I thought the idea of providing an explanation (as I did,
albeit nothing like as verbose as this one), together with example code
illustrating what I was trying to do was for people to assist me rather
than attempt to criticise at every opportunity or think I want them to
write it from scratch. I've read enough postings from people who have
not even bothered to show code as evidence they've tried, and the terse
responses.

So much for co-operation.

<snip>
>1. I will not have multiple exits from a function.

Then use if - which provides exactly the same functionality as a switch
with only one condition (unless I'm missing something).
....which you are. if() may be easier to code, but switch is faster. I'd
previously made this claim.

<snip>
As will an if, while or do. I think in absolute terms, any of them
will be faster than switch - but we're talking nano seconds here. See
how many loops you need to do with a test harness to get meainingful
timed results.
The main killers of my application at the moment are:

1. Some unbelievably long string handling.
2. Extending options.

I've recently discovered that long string handling is a bad bad thing,
and I'll be looking at using a big array of short(ish) strings and a join().

I was looking at extending options as I had originally described as both
an exercise in programming, and as trying to find out if it would give
me any speed advantage.

The short answer is it appears to be up to 10% slower than the DOM 0
method in IE and Firefox, but it's around 30 times quicker in Opera!

<snip>
Ah, so what you really want is a faster method than new Option()? I'm
not sure there is one specific method that is faster in all browsers.
You can't use createElement in IE, you don't want to use new Option()
because Opera is too slow and if you use innerHTML I'll bet some other
browser drags the chain. Test it and see.
I wasn't necessarily looking for or expecting to find one method that's
faster for all browsers, but being able to optimise this area for one
browser is a good thing.
Incidentally, if you decide to do that, don't use the += operator to
concatenate strings in IE, it is very slow (when used thousands of
times in a loop). Better to put the option HTML strings in an array
and use join().
I wasn't doing that but, as I've mentioned above, I will be looking at
join() for some other string handling.

<snip>

As you can probably gather, I've now found a solution that works (and
only two lines needed changing from my original posting). It's only a
(huge) speed improvement for Opera (and for IE it gets exponentially
slower - I may look at extending a chunk at a time), but it proves the
point to me.

Ian
Jul 4 '06 #5

This discussion thread is closed

Replies have been disabled for this discussion.