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">
- 下个月Win7正式“退休”,数据显示国内近60%电脑用户仍在使用
- Win10兼容安卓项目泡汤!微软做错了?
- 软件定义:英特尔携VMware重塑数据中心
- 软件定义网络SDN与网络可见性的挑战
- 谷歌重塑软件业务围剿苹果
- 谷歌无人驾驶汽车或将带来下一轮失业大潮
- 80后回忆录 那些年我们折腾过的IT玩意
- regex - How do I force nginx basic authentication for files starting with a certain mask? - Stack Overflow
- python - How to add a linear gradient to a QTableWidget? - Stack Overflow
- vuejs3 - Vue 3 Composition API data() - Generic idea required - Stack Overflow
- c# - Getting username of logged in user with NegotiateWindows domain credentials - Stack Overflow
- x86 - how does internal functions of kernel resolve after paging? - Stack Overflow
- javascript - AJAX implementation doesn't work properly for "like" button ASP.NET Core 2.1 - Stack Over
- c++ - Casting std::optional inferior type - Stack Overflow
- python - pickle port? loading a pickle with data when I've redefined the object code - Stack Overflow
- flutter - Exported Excel File is Blank When Using excel Package - Stack Overflow
- eclipse - Floodlight debug Exception in thread "main" java.lang.Error - Stack Overflow
innerHTML
. UseappendChild()
orinsertAdjacentElement()
to move the elements around themselves. – Barmar Commented 18 hours ago