Okay, I've been away for a little while. I've been watching the discussion here but also working at the function on my end. This is a solution that seems to work. The event handlers were being erased (rather, not copied over in the first place) by the cloneNode function, so that when the options were copied back over, the event handlers were missing.
My function might not be the most efficient one. I have a white belt in Javascript-fu, to be honest, while some solutions here look like they're at a black belt level. So my function might be primitive.
But I modified my idea to be able to (a) order by either innerHTML or by value, (b) be case sensitive or not, and (c) ignore the first option child (in case that first option is "Select one of the following" or some version of that phrase).
function orderSelectListItems(theSelectList, property = 'innerHTML', caseSensitive = false, ignoreFirstOption = false) {
var startHere = ignoreFirstOption ? 1 : 0; //if we skip the first option, start with the second (i.e. element 1)
var comparisonOutcome; //for localeCompare
var theDummyList = document.createElement('select');
while(theSelectList.options.length > startHere) {
theDummyList.appendChild(theSelectList.options[startHere]);
}
while(theDummyList.options.length > 0) { //while there are still elements in the dummy list, keep comparing and moving back to the real list
comparisonOutcome = null; //reset the comparison outcome for a new test
for(var optionCounter = startHere; optionCounter < theSelectList.options.length; optionCounter++) {
if((property == 'innerHTML') && (caseSensitive == true)) {
comparisonOutcome = theDummyList.options[0].innerHTML.localeCompare(theSelectList.options[optionCounter].innerHTML);
} else if((property == 'innerHTML') && (caseSensitive == false)) {
comparisonOutcome = theDummyList.options[0].innerHTML.toLocaleUpperCase().localeCompare(theSelectList.options[optionCounter].innerHTML.toLocaleUpperCase());
} else if((property == 'value') && (caseSensitive == true)) {
comparisonOutcome = theDummyList.options[0].value.localeCompare(theSelectList.options[optionCounter].value);
} else if((property == 'value') && (caseSensitive == false)) {
comparisonOutcome = theDummyList.options[0].value.toLocaleUpperCase().localeCompare(theSelectList.options[optionCounter].value.toLocaleUpperCase());
}
if(comparisonOutcome < 0) { //if the current comparison comes before the next element in the select list, insert the current one before it
theSelectList.insertBefore(theDummyList.options[0], theSelectList.options[optionCounter]);
break;
}
}
if(comparisonOutcome >= 0) { //if the current comparison hasn't found its place, that means it's the last one in order, so add it to the end
theSelectList.appendChild(theDummyList.options[0]);
}
} //we exit when all the options in theDummyList have been moved over
theDummyList.remove(); //delete the dummy list
}
Two things I could see that might make this more efficient, but I wasn't sure how to do it:
Is there a way to refer to a dynamic property? For instance, if I passed an argument to the function where property = innerHTML
or property = value
, can I then refer to it later by saying theSelectList.property
?
Can I also pass a function by argument, so instead of having an if/else branch for both innerHTML.toLocaleUpperCase().localeCompare()
and one without toLocaleUpperCase()
, I can just use innerHTML.passedFunction().localeCompare()
where passedFunction() is either toLocaleUpperCase()` or blank?
Being able to do these both would reduce my if/else block greatly.
Anyway, this is my working function so far. :?