javascript - Alphabetize options in a select list, retain event listeners - Stack Overflow
I need to alphabetize the options in a select list. I can't just switch the innerHTML and value attributes because some options have classes and event listeners attached. Here is what I have so far:
function alphabetizeSelectListItems(theOldList) {
var theNewList;
var insertOptionHere;
theNewList = theOldList.cloneNode(true);
while(theOldList.options.length > 0) {
theOldList.options[0].remove();
}
while(theNewList.options.length > 0) {
insertOptionHere = null;
for(var optionCounter = 0; optionCounter < theOldList.length; optionCounter++) {
if(theNewList.options[0].innerHTML.toLocaleUpperCase().localeCompare(theOldList.options[optionCounter].innerHTML.toLocaleUpperCase()) < 0) {
insertOptionHere = optionCounter;
break;
}
}
if(insertOptionHere == null) {
theOldList.appendChild(theNewList.options[0]);
} else {
theOldList.insertBefore(theNewList.options[0], theOldList.options[insertOptionHere]);
}
}
theNewList.remove();
}
This clones the old list into a temp element, empties the options from the old list, and then copies each option from the temp list back into the old list in its alphabetical place. Then it deletes the temp list.
Problem with this is it copies everything except the event listeners. So I have to use the same select list with the same options but move them around. I've looked at bubble sorting examples and tried to adapt them to option elements instead of array elements, but when I swap two elements, I have to store one in a temporary element, and that removes the event listener.
Is this possible? Is there a way to swap around select list options while retaining the event listeners?
I need to alphabetize the options in a select list. I can't just switch the innerHTML and value attributes because some options have classes and event listeners attached. Here is what I have so far:
function alphabetizeSelectListItems(theOldList) {
var theNewList;
var insertOptionHere;
theNewList = theOldList.cloneNode(true);
while(theOldList.options.length > 0) {
theOldList.options[0].remove();
}
while(theNewList.options.length > 0) {
insertOptionHere = null;
for(var optionCounter = 0; optionCounter < theOldList.length; optionCounter++) {
if(theNewList.options[0].innerHTML.toLocaleUpperCase().localeCompare(theOldList.options[optionCounter].innerHTML.toLocaleUpperCase()) < 0) {
insertOptionHere = optionCounter;
break;
}
}
if(insertOptionHere == null) {
theOldList.appendChild(theNewList.options[0]);
} else {
theOldList.insertBefore(theNewList.options[0], theOldList.options[insertOptionHere]);
}
}
theNewList.remove();
}
This clones the old list into a temp element, empties the options from the old list, and then copies each option from the temp list back into the old list in its alphabetical place. Then it deletes the temp list.
Problem with this is it copies everything except the event listeners. So I have to use the same select list with the same options but move them around. I've looked at bubble sorting examples and tried to adapt them to option elements instead of array elements, but when I swap two elements, I have to store one in a temporary element, and that removes the event listener.
Is this possible? Is there a way to swap around select list options while retaining the event listeners?
Share Improve this question edited 19 hours ago jonrsharpe 122k30 gold badges263 silver badges469 bronze badges asked 19 hours ago Steve G.Steve G. 4092 gold badges10 silver badges23 bronze badges 5 |2 Answers
Reset to default 1You can have an array of the elements and sort it. Then reintroduce the elements into the parent, using appendChild
which will move them without cloning.
From MDN: If the given child is a reference to an existing node in the document, appendChild() moves it from its current position to the new position
function alphabetizeSelectListItems(theOldList) {
const children = Array.from(theOldList.options);
children.sort(function(a, b) {
return a.innerText.toLocaleUpperCase().localeCompare(b.innerText.toLocaleUpperCase())
})
children.forEach(child => theOldList.appendChild(child));
}
alphabetizeSelectListItems(select)
<select id="select">
<option>Alpha</option>
<option>Gamma</option>
<option>Epsilon</option>
<option>Beta</option>
</select>
Update
I'm not exactly certain if you wanted the sorting when loading or by the user so my answer covers both scenarios.
Event delegation is a programming paradigm that requires:
The element that is registered to an event should be an ancestor✼ of the element intended for interaction (eg. a
<button>
to "click", an<input>
to type into, etc).Write the event handler to conditionally accept only the elements intended for interaction. Exclude the rest by not including them.
✼An ancestor element contains children elements.
The advantages are as follows:
Any children elements of the registered ancestor can be delegated to the registered event(s). That also includes any elements added dynamically in the future.
There is no limit to the amount of elements that events can be delegated to.
These advantages give you almost full control of what does what, how, when, and why.
Details are commented in the example.
User Sorts with <button>
// Reference <form>
const ui = document.forms.ui;
/**
* Reference all form controls in <form>
* In this particular layout they are:
* - <button>
* - <select>
* - <output>
*/
const io = ui.elements;
// Reference <select>
const pick = io.pick;
// Reference <output>
const logo = io.logo;
// Array of <option>s
const opts = Array.from(pick.children);
/**
* For some reason <select> wouldn't stay in
* order. I have a workaround:
* 1. Add [multiple] attribute to <select> tag.
* 2. Next, after <select> has been referenced
* remove the attribute.
*/
pick.removeAttribute("multiple");
/**
* This event handler is triggered when <form> is
* "click"ed. It delegates the "click" event to the
* button#sort and excludes all other elements.
* It does this by setting conditions...
*/
const order = e => {
/**
* e.target is the element that the user clicked.
* So this handler will do nothing unless the user
* clicked an element with the #id of "sort".
*/
if (e.target.id === "sort") {
// Sort the array of <option>s...
opts.sort((a, b) => a.value.localeCompare(b.value))
// Add them back to select#pick...
.forEach(o => pick.append(o));
// Adjust <select> and <output> to the first position.
opts[0].selected = true;
logo.value = pick.options[pick.selectedIndex].text.slice(0, 2);
}
};
/**
* This event handler just displays a larger icon.
* It was added to show that the <select> is safe.
*/
const show = e => {
if (e.target.id === "pick") {
logo.value = e.target.options[e.target.selectedIndex].text.slice(0, 2);
}
};
ui.addEventListener("click", order);
ui.addEventListener("change", show);
:root {
font: 3ch/1.5 "Segoe UI"
}
form {
display: flex;
justify-content: space-evenly;
align-items: center
}
select,
output {
display: block;
padding: 5px;
font: inherit
}
#pick {
min-height: 2.25rem;
cursor: pointer;
}
#logo {
font-size: 3rem
}
<form id="ui">
<button id="sort" type="button">Sort</button>
<select id="pick" multiple>
<option value="dragon">
- 覆盖7大手机品牌近4亿安卓用户,“互传联盟”让分享更容易
- c++ - Why is my OpenGL application rendering a 3D model with unexpected transparency? - Stack Overflow
- How can I add a Linux system-level API without recompiling the kernel? - Stack Overflow
- dagger hilt - Datadog (Android SDK) Logs not going through to the dashboard - Stack Overflow
- audio - How can i improve my sound module in Java? - Stack Overflow
- python - Why does my plot have criss-crossing lines when I convert the index from string to datetime? - Stack Overflow
- python - ConnectionError(err, request=request) requests.exceptions.ConnectionError: ('Connection aborted.', Remo
- Flutter iOS UrlLauncher EXC_BAD_ACCESS - Stack Overflow
- express - What is the point of http-only cookies and how to make your authentication secure? - Stack Overflow
- rust - How Can I Efficiently Render an Emulator Framebuffer in an Iced Widget at 60 FPS? - Stack Overflow
- php - Get WooCommerce custom payment gateway values in process_payment function for Blocks checkout - Stack Overflow
- Keeping Data In Denormalized NoSql Databases (CassandraScyllaDb) Tables In Sync? - Stack Overflow
- javascript - Why does every object in BabylonJS require a name? - Stack Overflow
- android - OneSignal Not Subscribing Users - Stack Overflow
- python - Galaga game not running due to an empty range - Stack Overflow
- apache - is it possible to graceful single process in passenger rails app? - Stack Overflow
- python - preprocessing strategies for OCR (pytesseract) character recognition - Stack Overflow
innerHTML
. UseappendChild()
orinsertAdjacentElement()
to move the elements around themselves. – Barmar Commented 18 hours ago