diff --git a/js/rulesAssistant/conditionEditor.js b/js/rulesAssistant/conditionEditor.js
index eda0f1d63f27463f00e35e01595f1b3ef8caa342..349009179603ded4ffdfc94634288bb557f6a601 100644
--- a/js/rulesAssistant/conditionEditor.js
+++ b/js/rulesAssistant/conditionEditor.js
@@ -533,19 +533,22 @@ App.RA.Activation.Editor = (function() {
 
 	/**
 	 * @typedef {"sub" | "div" | "eq" | "neq" | "gt" | "gte" | "lt" | "lte"| "substr"} RulePairComparators
-	 * @type {Map<RulePairComparators, string>}
+	 * @typedef {object} RulePairComparatorDisplay
+	 * @property {string} key
+	 * @property {string} name
+	 * @type {RulePairComparatorDisplay[]}
 	 */
-	const rulePairComparators = new Map([
-		["eq", "="],
-		["neq", "≠"],
-		["lt", "<"],
-		["gt", ">"],
-		["lte", "⩽"],
-		["gte", "⩾"],
-		["sub", "-"],
-		["div", "/"],
-		["substr", "Contains"],
-	]);
+	const rulePairComparators = [
+		{key: "eq", name: "="},
+		{key: "neq", name: "≠"},
+		{key: "lt", name: "<"},
+		{key: "gt", name: ">"},
+		{key: "lte", name: "⩽"},
+		{key: "gte", name: "⩾"},
+		{key: "sub", name: "-"},
+		{key: "div", name: "/"},
+		{key: "substr", name: "Contains"},
+	];
 
 	class RulePair extends RuleContainer {
 		/**
@@ -574,30 +577,11 @@ App.RA.Activation.Editor = (function() {
 			this._conditionalDropLocation(div, this._child1, child => this.child1 = child);
 
 			// operator
-			let matchFound = false;
-			const select = document.createElement("select");
-			for (const [key, name] of rulePairComparators) {
-				const el = document.createElement("option");
-				el.value = key;
-				el.textContent = name;
-				if (this.mode === key) {
-					el.selected = true;
-					matchFound = true;
-				}
-				select.append(el);
-			}
-			if (!matchFound) {
-				select.selectedIndex = -1;
-			}
-			select.onchange = () => {
-				/** @type {HTMLSelectElement} */
+			div.append(App.UI.DOM.makeSelect(rulePairComparators, this.mode, mode => {
 				// @ts-ignore
-				const option = select.children.item(select.selectedIndex);
-				// @ts-ignore
-				this.mode = option.value;
+				this.mode = mode;
 				refreshEditor();
-			};
-			div.append(select);
+			}));
 
 			// element 2
 			this._conditionalDropLocation(div, this._child2, child => this.child2 = child);
@@ -930,35 +914,21 @@ App.RA.Activation.Editor = (function() {
 			App.UI.DOM.appendNewElement("span", span, this.mode === "assignment" ? "Assignment" : "Slave", ["rule-right-margin"]);
 
 			// values
-			let matchFound = false;
-			let select = document.createElement("select");
-			for (const [key, value] of this._getterMap) {
-				if (value.visible && !value.visible()) {
-					continue;
-				}
-				let el = document.createElement("option");
-				el.value = key;
-				el.textContent = value.name;
-				if (value.enabled) {
-					el.disabled = !value.enabled();
-				}
-				if (this.key === key) {
-					el.selected = true;
-					matchFound = true;
+			/**
+			 * @type {selectOption[]}
+			 */
+			const options = [];
+			this._getterMap.forEach((getter, key) => {
+				if (!getter.visible || getter.visible()) {
+					options.push({
+						key: key, name: getter.name, enabled: !getter.enabled || getter.enabled()
+					});
 				}
-				select.append(el);
-			}
-			if (!matchFound) {
-				select.selectedIndex = -1;
-			}
-			select.onchange = () => {
-				/** @type {HTMLSelectElement} */
-				// @ts-ignore
-				const option = select.children.item(select.selectedIndex);
-				this.key = option.value;
+			});
+			span.append(App.UI.DOM.makeSelect(options, this.key, key => {
+				this.key = key;
 				refreshEditor();
-			};
-			span.append(select);
+			}));
 			return span;
 		}
 
diff --git a/js/ui/select.js b/js/ui/select.js
index 5de606ff1e3ce635f6085944c848a075864f1c9b..4eb0e69d4a075fc963ec377e59cc18e8eb8bc203 100644
--- a/js/ui/select.js
+++ b/js/ui/select.js
@@ -1,13 +1,19 @@
+/**
+ * @typedef {object} selectOption
+ * @property {string} key
+ * @property {string} name
+ * @property {boolean} [enabled]
+ */
+
 /**
  * Creates a new dropdown with available and unavailable options.
- * @param {Array<{value: string, name: string}>} options A list of options to display, where `value` sets the property and `name` is displayed.
- * @param {string} selected The property to change upon selection.
- * @param {function():void} [onchange] Any custom function to run upon selection.
- * @param {Object} [object] Any object `property` belongs to, if not the default `V`.
+ * @param {Array<selectOption>} options A list of options to display
+ * @param {string} selected
+ * @param {function(string):void} onchange
  *
  * @returns {HTMLSelectElement}
  */
-App.UI.DOM.makeSelect = function(options, selected, onchange, object = V) {
+App.UI.DOM.makeSelect = function(options, selected, onchange) {
 	const select = document.createElement("select");
 	let matchFound = false;
 
@@ -15,13 +21,17 @@ App.UI.DOM.makeSelect = function(options, selected, onchange, object = V) {
 		const option = document.createElement("option");
 
 		option.text = choice.name;
-		option.value = choice.value;
+		option.value = choice.key;
 
-		if (object[selected] === choice.value) {
+		if (selected === choice.key) {
 			option.selected = true;
 			matchFound = true;
 		}
 
+		if ("enabled" in choice && !choice.enabled) {
+			option.disabled = true;
+		}
+
 		select.append(option);
 	}
 
@@ -29,13 +39,7 @@ App.UI.DOM.makeSelect = function(options, selected, onchange, object = V) {
 		select.selectedIndex = -1;
 	}
 
-	select.onchange = () => {
-		object[selected] = select.options[select.selectedIndex].value;
-
-		if (onchange) {
-			onchange();
-		}
-	};
+	select.onchange = () => onchange(select.value);
 
 	return select;
 };
diff --git a/src/events/RE/reNickname.js b/src/events/RE/reNickname.js
index af3d36373850c43e77799fb260cc78b22848b317..80e0e7aba13a95a981762d5932067214e1a37341 100644
--- a/src/events/RE/reNickname.js
+++ b/src/events/RE/reNickname.js
@@ -81,48 +81,28 @@ App.Events.RENickname = class RENickname extends App.Events.BaseEvent {
 			function selectCategory(cheat) {
 				const el = new DocumentFragment();
 				if (cheat) {
-					const choice = App.UI.DOM.appendNewElement("span", el, `Select a category of nicknames `);
-					const select = App.UI.DOM.appendNewElement("select", choice);
-					let matchFound = false;
+					App.UI.DOM.appendNewElement("span", el, `Select a category of nicknames `);
+					const options = [];
 					for (const category of qualifiedNicknames.keys()) {
-						const option = App.UI.DOM.appendNewElement("option", select, category);
-						option.value = category;
-						if (option.value === seed) {
-							option.selected = true;
-							matchFound = true;
-						}
-					}
-					if (!matchFound) {
-						select.selectedIndex = -1;
+						options.push({key: category, name: category});
 					}
-					select.onchange = () => {
-						const O = select.options[select.selectedIndex];
-						seed = O.value;
+					el.append(App.UI.DOM.makeSelect(options, seed, value => {
+						seed = value;
 						jQuery(intro).empty().append(introPassage());
-					};
+					}));
 				}
 				return el;
 			}
 			function selectNickname(cheat) {
 				const el = new DocumentFragment();
 				if (cheat) {
-					const select = App.UI.DOM.appendNewElement("select", el);
-					let matchFound = false;
+					const options = [];
 					for (const category of nicknameArray) {
-						const option = App.UI.DOM.appendNewElement("option", select, `'${category}'`);
-						option.value = category;
-						if (option.value === nickname) {
-							option.selected = true;
-							matchFound = true;
-						}
-					}
-					if (!matchFound) {
-						select.selectedIndex = -1;
+						options.push({key: category, name: `'${category}'`});
 					}
-					select.onchange = () => {
-						const O = select.options[select.selectedIndex];
-						nickname = O.value;
-					};
+					el.append(App.UI.DOM.makeSelect(options, nickname, name => {
+						nickname = name;
+					}));
 				} else {
 					App.UI.DOM.appendNewElement("span", el, `${nickname} `, "pink");
 				}
diff --git a/src/events/randomEvent.js b/src/events/randomEvent.js
index 462608e912ef4139484e39d00436f29a9bfeff11..348b86548877ac1767af54ceb3952aeb603fe723 100644
--- a/src/events/randomEvent.js
+++ b/src/events/randomEvent.js
@@ -456,21 +456,17 @@ App.Events.playRandomIndividualEvent = function() {
 			if (V.RIESkip.length > 0) {
 				countPara.append(` An event has already played for ${toSentence(V.RIESkip.map(s => SlaveFullName(getSlave(s))))}, so they are not eligible to play another.`);
 			}
+
 			const slaveDiv = App.UI.DOM.appendNewElement("div", d, "Show events for this slave: ");
-			const slaveDropdown = App.UI.DOM.appendNewElement("select", slaveDiv);
+			const options = [];
 			const startingSlave = eligibleSlaves.random();
 			for (const s of eligibleSlaves) {
-				const choice = App.UI.DOM.appendNewElement("option", slaveDropdown, SlaveFullName(s));
-				choice.value = s.ID.toString();
-				if (s.ID === startingSlave.ID) {
-					choice.selected = true;
-				}
+				options.push({key: s.ID.toString(), name: SlaveFullName(s)});
 			}
-			slaveDropdown.onchange = () => {
-				const O = slaveDropdown.options[slaveDropdown.selectedIndex];
-				const slaveID = parseInt(O.value);
-				writeEventList(getSlave(slaveID));
-			};
+			slaveDiv.append(App.UI.DOM.makeSelect(options, startingSlave.ID.toString(), slaveID => {
+				writeEventList(getSlave(parseInt(slaveID)));
+			}));
+
 			App.UI.DOM.appendNewElement("p", d, "One of the following individual events would have been chosen for this slave.");
 
 			const linkList = App.UI.DOM.appendNewElement("div", d, '', "event-section");
diff --git a/src/events/reRecruit.js b/src/events/reRecruit.js
index a205f532c2af58e6178ed63e195e9b0cd2aa8ecf..f182c1b76f038dace1f3307d085cd45b975cfbd7 100644
--- a/src/events/reRecruit.js
+++ b/src/events/reRecruit.js
@@ -112,18 +112,13 @@ App.Events.RERecruit = class RERecruit extends App.Events.BaseEvent {
 		if (V.debugMode && V.debugModeEventSelection) {
 			const el = App.UI.DOM.appendNewElement("span", node);
 			App.UI.DOM.appendNewElement("span", el, `One of the following recruitment events would have appeared: `);
-			const select = App.UI.DOM.appendNewElement("select", el);
 			const evList = this.eventList.filter(e => App.Events.canExecute(e));
-			for (const ev of evList) {
-				const choice = App.UI.DOM.appendNewElement("option", select, ev.eventName);
-				choice.value = ev.eventName;
-			}
-			select.selectedIndex = -1;
-			select.onchange = () => {
-				const O = select.options[select.selectedIndex];
+			el.append(App.UI.DOM.makeSelect(evList.map(e => {
+				return {key: e.eventName, name: e.eventName};
+			}), null, e => {
 				el.remove();
-				evList.find(ev => ev.eventName === O.value).execute(node);
-			};
+				evList.find(ev => ev.eventName === e).execute(node);
+			}));
 		} else {
 			// forward execution to the delegate event
 			this.params.event.execute(node);
diff --git a/src/facilities/surgery/surgeryPassageExotic.js b/src/facilities/surgery/surgeryPassageExotic.js
index d0db94c1977012723fb543181cea8a6d245194ba..01d28af97829faa79bdbfcfe217aba43cc3a2b5b 100644
--- a/src/facilities/surgery/surgeryPassageExotic.js
+++ b/src/facilities/surgery/surgeryPassageExotic.js
@@ -105,10 +105,10 @@ App.UI.surgeryPassageExotic = function(slave, refreshParent, cheat = false) {
 			function retroVirus() {
 				const el = new DocumentFragment();
 				const slaveGeneList = App.UI.DOM.appendNewElement("ul", el);
-				const select = App.UI.DOM.makeElement("select", null, "choices");
 				const canEditGenes = slave.indentureRestrictions === 0 && slave.health.health >= 0;
-				select.classList.add("rajs-list");
 				const description = App.UI.DOM.appendNewElement("div", el, null);
+
+				const options = /** @type {selectOption[]} */ [];
 				for (const gene in slave.geneticQuirks) {
 					const geneData = App.Data.geneticQuirks.get(gene);
 
@@ -132,18 +132,13 @@ App.UI.surgeryPassageExotic = function(slave, refreshParent, cheat = false) {
 						continue;
 					}
 					if (canEditGenes) {
-						const choice = App.UI.DOM.appendNewElement("option", select, capFirstChar(geneData.title));
-						choice.value = gene;
-						select.append(choice);
+						options.push({key: gene, name: capFirstChar(geneData.title)});
 					}
 				}
 				if (canEditGenes) {
-					select.selectedIndex = -1;
-					select.onchange = () => {
-						// selectedGene = select.options[select.selectedIndex];
-						jQuery(description).empty().append(describeGene(select.value));
-					};
-					el.append(select);
+					el.append(App.UI.DOM.makeSelect(options, null, gene => {
+						jQuery(description).empty().append(describeGene(gene));
+					}));
 				}
 
 				return el;
diff --git a/src/facilities/toyShop/toyShop.js b/src/facilities/toyShop/toyShop.js
index 4e910d977a1da986d7ac2637f112a4c07f12b59d..300d5b90b6cf74ef7047326a836ffa9ff0b19b36 100644
--- a/src/facilities/toyShop/toyShop.js
+++ b/src/facilities/toyShop/toyShop.js
@@ -326,32 +326,21 @@ App.UI.toyShop = function() {
 	/**
 	 * @param {toy} toy
 	 * @param {string} itemKey
-	 * @returns {DocumentFragment}
+	 * @returns {HTMLElement}
 	 */
 	function selectDesign(toy, itemKey) {
-		const el = new DocumentFragment();
-		const choice = App.UI.DOM.appendNewElement("span", el, ` or choose an existing design to edit `);
-		const select = App.UI.DOM.appendNewElement("select", choice);
-		let matchFound = false;
+		const choice = App.UI.DOM.makeElement("span", ` or choose an existing design to edit `);
+		const options = [];
 		for (const [key, values] of V.customItem[itemKey]) {
-			const option = App.UI.DOM.appendNewElement("option", select, values.name);
-			option.value = key;
-			if (option.value === toy.name) {
-				option.selected = true;
-				matchFound = true;
-			}
-		}
-		if (!matchFound) {
-			select.selectedIndex = -1;
+			options.push({key: key, name: values.name});
 		}
-		select.onchange = () => {
-			const O = select.options[select.selectedIndex];
-			toy.selected = O.value;
+		choice.append(App.UI.DOM.makeSelect(options, toy.name, key => {
+			toy.selected = key;
 			toy.name = toy.selected;
 			toy.data = V.customItem[itemKey].get(toy.selected);
 			refresh();
-		};
-		return el;
+		}));
+		return choice;
 	}
 
 	function refresh() {
diff --git a/src/futureSocieties/fsPassage.js b/src/futureSocieties/fsPassage.js
index dbf0835259b3b482b5b6f69ac1bbf8c17bcb8855..31e77af51fb4d8151f6d1c534f2ce13526773951 100644
--- a/src/futureSocieties/fsPassage.js
+++ b/src/futureSocieties/fsPassage.js
@@ -372,21 +372,14 @@ App.UI.fsPassage = function() {
 					r.push(`${arc.FSSupremacistRace} superiority.`);
 				}
 				r.push(`Select race:`);
-				const select = document.createElement("select");
-				r.push(select);
+				const options = [];
 				for (const race of App.Utils.getRaceArrayWithoutParamRace(arc.FSSubjugationistRace)) { // Subjugation race cannot be superior, so remove
-					const choice = App.UI.DOM.appendNewElement("option", select, capFirstChar(race));
-					choice.value = race;
-					if (race === arc.FSSupremacistRace) {
-						choice.selected = true;
-					}
+					options.push({key: race, name: capFirstChar(race)});
 				}
-
-				select.onchange = () => {
-					const O = select.options[select.selectedIndex];
-					arc.FSSupremacistRace = O.value;
+				r.push(App.UI.DOM.makeSelect(options, arc.FSSupremacistRace, race => {
+					arc.FSSupremacistRace = /** @type {FC.Race} */ (race);
 					App.UI.reload();
-				};
+				}));
 			} else {
 				/* <span class="note"><span style="font-weight:Bold">Racial Supremacism:</span> a belief in the superiority of a chosen race.</span>*/
 			}
@@ -415,22 +408,15 @@ App.UI.fsPassage = function() {
 					r.push(`${arc.FSSubjugationistRace} inferiority.`);
 				}
 				r.push(`Select race:`);
-				const select = document.createElement("select");
-				r.push(select);
 
+				const options = [];
 				for (const race of App.Utils.getRaceArrayWithoutParamRace(arc.FSSupremacistRace)) { // Superior race cannot be subj, so remove
-					const choice = App.UI.DOM.appendNewElement("option", select, capFirstChar(race));
-					choice.value = race;
-					if (race === arc.FSSubjugationistRace) {
-						choice.selected = true;
-					}
+					options.push({key: race, name: capFirstChar(race)});
 				}
-
-				select.onchange = () => {
-					const O = select.options[select.selectedIndex];
-					arc.FSSubjugationistRace = O.value;
+				r.push(App.UI.DOM.makeSelect(options, arc.FSSubjugationistRace, race => {
+					arc.FSSubjugationistRace = race;
 					App.UI.reload();
-				};
+				}));
 			} else {
 				/* <span class="note"><span style="font-weight:Bold">Racial Subjugationism:</span> a belief in the inferiority of a subject race.</span>*/
 			}
diff --git a/src/gui/options/optionsGroup.js b/src/gui/options/optionsGroup.js
index c49892ec2149452801e93eb75c9acdc3e5275a03..b7403c7b291452cc87043757b2b1925de9405f4b 100644
--- a/src/gui/options/optionsGroup.js
+++ b/src/gui/options/optionsGroup.js
@@ -268,36 +268,21 @@ App.UI.OptionsGroup = (function() {
 					buttonGroup.append(button);
 				}
 			} else {
-				let matchFound = false;
-				let select = document.createElement("select");
-
-				for (const value of this.valuePairs) {
-					let el = document.createElement("option");
-					el.textContent = value.name;
-					el.value = value.value;
-					if (this.object[this.property] === value.value) {
-						el.selected = true;
-						matchFound = true;
+				const options = this.valuePairs.map(value => {
+					return {key: value.value, name: value.name};
+				});
+				buttonGroup.append(App.UI.DOM.makeSelect(options, this.object[this.property], value => {
+					if (!isNaN(Number(value))) {
+						// @ts-ignore
+						value = Number(value);
 					}
-					select.appendChild(el);
-				}
-				if (!matchFound) {
-					select.selectedIndex = -1;
-				}
-				select.onchange = () => {
-					const O = select.options[select.selectedIndex];
-					if (isNaN(Number(O.value))) {
-						this.object[this.property] = O.value;
-					} else {
-						this.object[this.property] = Number(O.value);
-					}
-					const originalObj = this.valuePairs.find(obj => obj.name === O.textContent);
+					this.object[this.property] = value;
+					const originalObj = this.valuePairs.find(obj => obj.value === value);
 					if (originalObj && typeof originalObj.callback === "function") {
 						originalObj.callback(originalObj.value);
 					}
 					refresh();
-				};
-				buttonGroup.append(select);
+				}));
 			}
 
 			if (this.textbox) {
diff --git a/src/interaction/prostheticConfig.js b/src/interaction/prostheticConfig.js
index 44147410af15659fb89f0fca0c1fedfcbf71e45b..9a64ec644b246b8b9dc56fb3a13121a187343c6b 100644
--- a/src/interaction/prostheticConfig.js
+++ b/src/interaction/prostheticConfig.js
@@ -324,30 +324,19 @@ App.UI.prostheticsConfig = function(slave) {
 		return f;
 
 		function tailSelect() {
-			const frag = new DocumentFragment();
-			const sortedTails = Array.from(App.Data.modTails.keys()).sort((a, b) => a > b ? 1 : -1);
-			const select = App.UI.DOM.appendNewElement("select", frag);
-			let matchFound = false;
-			for (const shape of sortedTails) {
-				const option = App.UI.DOM.appendNewElement("option", select, App.Data.modTails.get(shape).animal);
-				option.value = shape;
-				if (option.value === slave.tailShape) {
-					option.selected = true;
-					matchFound = true;
-				}
-			}
-			if (!matchFound) {
-				select.selectedIndex = -1;
-			}
-			select.onchange = () => {
+			const sortedTails = Array.from(App.Data.modTails.keys())
+				.sort((a, b) => a > b ? 1 : -1)
+				.map(tail => {
+					return {key: tail, name: App.Data.modTails.get(tail).animal};
+				});
+			return App.UI.DOM.makeSelect(sortedTails, slave.tailShape, tail => {
 				V.prostheticsConfig = "attachTail";
 				slave.tail = "mod";
-				slave.tailShape = select.options[select.selectedIndex].value;
+				slave.tailShape = /** @type {FC.TailShape} */ (tail);
 				slave.tailColor = (slave.tailColor === "none") ? slave.hColor : slave.tailColor; // if color not set yet, match hair.
 				cashX(forceNeg(V.modCost), "slaveMod", slave);
 				App.UI.reload();
-			};
-			return frag;
+			});
 		}
 	}
 
diff --git a/src/js/slaveListing.js b/src/js/slaveListing.js
index b8809b420e4f32bab016593c5a7a28d8f927b72d..c1172b62ab8ab6a0ba661320d8bcd29aa35e7357 100644
--- a/src/js/slaveListing.js
+++ b/src/js/slaveListing.js
@@ -549,10 +549,13 @@ App.UI.SlaveList.sortingLinks = function(passage) {
 	let span = App.UI.DOM.makeElement("span", "Sort by: ");
 	let order = ["devotion", "trust", "name", "assignment", "seniority", "actualAge", "visualAge", "physicalAge", "weeklyIncome", "health", "weight", "muscles", "intelligence", "sexDrive", "pregnancy", "prestige"];
 	const orderMap = order.map(so => {
-		return {value: so, name: textify(so)};
+		return {key: so, name: capFirstChar(so)};
 	});
 
-	div.append(App.UI.DOM.makeSelect(orderMap, 'sortSlavesBy', App.UI.reload));
+	div.append(App.UI.DOM.makeSelect(orderMap, V.sortSlavesBy, val => {
+		V.sortSlavesBy = val;
+		App.UI.reload();
+	}));
 
 	span = App.UI.DOM.makeElement("span", " Sort direction: ");
 	order = ["descending", "ascending"];
diff --git a/src/markets/specificMarkets/customSlaveMarket.js b/src/markets/specificMarkets/customSlaveMarket.js
index 139c7d8a6c366b63b6b118e3c9fdf18381d766f4..a9b76aeb00edf580c1e03fbb0d5dbc2bd51c6b7f 100644
--- a/src/markets/specificMarkets/customSlaveMarket.js
+++ b/src/markets/specificMarkets/customSlaveMarket.js
@@ -62,33 +62,23 @@ App.Markets["Custom Slave"] = function() {
 		createDescription(el, description, "age");
 
 		// Choices
-		const select = document.createElement("select");
+		const options = [];
 		for (let i = 0; i < ages.length; i++) {
 			const high = ages[i];
-			const low = (ages[i - 1] + 1 ) || (ages[i] - 1); // First element of array has nothing before it, obviously, so display low as one less than high.
+			const low = (ages[i - 1] + 1) || (ages[i] - 1); // First element of array has nothing before it, obviously, so display low as one less than high.
 			if (low < V.minimumSlaveAge) {
 				continue;
 			} else if (high > V.retirementAge) {
-				const option = document.createElement("option");
-				option.text = `${low}+`;
-				option.value = low.toString();
-				select.append(option);
+				options.push({key: low.toString(), name: `${low}+`});
 				break;
 			}
 
-			const option = document.createElement("option");
-			option.text = `${low}-${high}`;
-			option.value = high.toString();
-			if (slave.age === high) {
-				option.selected = true;
-			}
-			select.append(option);
+			options.push({key: high.toString(), name: `${low}-${high}`});
 		}
-		select.onchange = () => {
-			slave.age = Number(select.options[select.selectedIndex].value);
+		el.append(App.UI.DOM.makeSelect(options, slave.age.toString(), a => {
+			slave.age = Number(a);
 			jQuery("#age-text").empty().append(description());
-		};
-		el.append(select);
+		}));
 		return el;
 
 		function description() {
@@ -297,32 +287,18 @@ App.Markets["Custom Slave"] = function() {
 	function race() {
 		const el = document.createElement("div");
 		const slaveProperty = "race";
-		const choices = new Map([
-			["ethnicity is unimportant", "Ethnicity is unimportant"],
-		]);
+		const choices = [{key: "ethnicity is unimportant", name: "Ethnicity is unimportant"}];
 		for (const [race, capRace] of App.Data.misc.filterRacesPublic) {
-			choices.set(race, capRace);
+			choices.push({key: race, name: capRace});
 		}
 
 		createDescription(el, description, slaveProperty);
 
 		// Choices
-
-		const select = document.createElement("select");
-		for (const [value, text] of choices) {
-			const option = document.createElement("option");
-			option.text = text;
-			option.value = value;
-			if (slave.race === option.value) {
-				option.selected = true;
-			}
-			select.append(option);
-		}
-		select.onchange = () => {
-			slave.race = select.options[select.selectedIndex].value;
+		el.append(App.UI.DOM.makeSelect(choices, slave.race, r => {
+			slave.race = r;
 			jQuery("#race-text").empty().append(description());
-		};
-		el.append(select);
+		}));
 
 		function description() {
 			const el = new DocumentFragment();
@@ -345,31 +321,18 @@ App.Markets["Custom Slave"] = function() {
 	function skin() {
 		const el = document.createElement("div");
 		const slaveProperty = "skin";
-		const choices = new Map([
-			["left natural", "Left natural"]
-		]);
+		const choices = [{key: "left natural", name: "Left natural"}];
 		for (const skin of App.Medicine.Modification.naturalSkins) {
-			choices.set(skin, capFirstChar(skin));
+			choices.push({key: skin, name: capFirstChar(skin)});
 		}
 
 		createDescription(el, description, slaveProperty);
 
 		// Choices
-		const select = document.createElement("select");
-		for (const [value, text] of choices) {
-			const option = document.createElement("option");
-			option.text = text;
-			option.value = value;
-			if (slave.skin === option.value) {
-				option.selected = true;
-			}
-			select.append(option);
-		}
-		select.onchange = () => {
-			slave.skin = select.options[select.selectedIndex].value;
+		el.append(App.UI.DOM.makeSelect(choices, slave.skin, s => {
+			slave.skin = s;
 			jQuery("#skin-text").empty().append(description());
-		};
-		el.append(select);
+		}));
 
 		function description() {
 			const el = new DocumentFragment();
@@ -942,38 +905,26 @@ App.Markets["Custom Slave"] = function() {
 	function nationality() {
 		const el = document.createElement("div");
 		const slaveProperty = "nationality";
-		const choices = new Map([
-			["slave", "Slave"],
-			["Stateless", "Stateless"],
-			["Nationality is unimportant", "Nationality is unimportant"],
-		]);
+		const choices = [{key: "slave", name: "Slave"},
+			{key: "Stateless", name: "Stateless"},
+			{key: "Nationality is unimportant", name: "Nationality is unimportant"},
+		];
 		for (const nationality of App.Data.misc.baseNationalities) {
-			choices.set(nationality, nationality);
+			choices.push({key: nationality, name: nationality});
 		}
 
 		createDescription(el, description, slaveProperty);
 
 		// Choices
-		const select = document.createElement("select");
-		for (const [value, text] of choices) {
-			const option = document.createElement("option");
-			option.text = text;
-			option.value = value;
-			if (slave.nationality === option.value) {
-				option.selected = true;
-			}
-			select.append(option);
-		}
-		select.onchange = () => {
-			slave.nationality = select.options[select.selectedIndex].value;
+		el.append(App.UI.DOM.makeSelect(choices, slave.nationality, nat => {
+			slave.nationality = nat;
 			jQuery("#nationality-text").empty().append(description());
-		};
-		el.append(select);
+		}));
 
 		function description() {
-			for (const [value, text] of choices) {
-				if (slave.nationality === value) {
-					return `${text}. `;
+			for (const choice of choices) {
+				if (slave.nationality === choice.key) {
+					return `${choice.name}. `;
 				}
 			}
 		}
diff --git a/src/markets/specificMarkets/prestigiousSlave.js b/src/markets/specificMarkets/prestigiousSlave.js
index f8e3e6bf4052a2aeec6bc92255159745dd42db2e..20e721233a79f1111bbd254f8d6908fe5d76cf97 100644
--- a/src/markets/specificMarkets/prestigiousSlave.js
+++ b/src/markets/specificMarkets/prestigiousSlave.js
@@ -49,19 +49,12 @@ App.Markets["Prestigious Slave"] = function() {
 				content.append(passage());
 			};
 			const cheatOptions = (V.seeDicks > 0) ? options.concat(...dickOptions) : Array.from(options);
-			const slaveDropdown = App.UI.DOM.appendNewElement("select", frag);
-			for (const o of cheatOptions) {
-				const choice = App.UI.DOM.appendNewElement("option", slaveDropdown, capFirstChar(o));
-				choice.value = o;
-				if (seed === o) {
-					choice.selected = true;
-				}
-			}
-			slaveDropdown.onchange = () => {
-				const O = slaveDropdown.options[slaveDropdown.selectedIndex];
-				seed = O.value;
+			frag.append(App.UI.DOM.makeSelect(cheatOptions.map(v => {
+				return {key: v, name: capFirstChar(v)};
+			}), seed, v => {
+				seed = v;
 				reload();
-			};
+			}));
 			App.UI.DOM.appendNewElement("span", frag, App.UI.DOM.link(" Refresh slave", reload));
 		}
 		slave = makeSlave(seed);
diff --git a/src/npc/startingGirls/startingGirls.js b/src/npc/startingGirls/startingGirls.js
index 0d9ed58d2837caa395ef9ada7e3716877d9f1d40..7fec854b2252b3b7e6cdaddffe3d8cdd14265d4c 100644
--- a/src/npc/startingGirls/startingGirls.js
+++ b/src/npc/startingGirls/startingGirls.js
@@ -1604,20 +1604,15 @@ App.StartingGirls.makeCareerFilterPulldown = function() {
 	const frag = new DocumentFragment();
 	frag.append(`Filter by desired bonus: `);
 
-	const select = document.createElement("select");
-	select.style.fontStyle = "normal";
+	const options = [];
 	for (const cat of App.StartingGirls.careerBonusFilters.keys()) {
-		const choice = App.UI.DOM.appendNewElement("option", select, cat);
-		if (App.StartingGirls.careerFilter === cat) {
-			choice.selected = true;
-		}
+		options.push({key: cat, name: cat});
 	}
-
-	select.onchange = () => {
-		App.StartingGirls.careerFilter = select.value;
+	frag.append(App.UI.DOM.makeSelect(options, App.StartingGirls.careerFilter, cat => {
+		App.StartingGirls.careerFilter = cat;
 		App.UI.reload();
-	};
-	frag.append(select);
+	}));
+
 	return frag;
 };
 
diff --git a/src/npc/startingGirls/startingGirlsPassage.js b/src/npc/startingGirls/startingGirlsPassage.js
index 9d158c14c4e70a6bfc10cafd181ab34ea1d00071..b6ef844e4bbd59402b0715b4694b5ce8250aa9b4 100644
--- a/src/npc/startingGirls/startingGirlsPassage.js
+++ b/src/npc/startingGirls/startingGirlsPassage.js
@@ -339,18 +339,20 @@ App.StartingGirls.passage = function() {
 				`Start over with a finalized slave's relative`,
 				() => {
 					const el = new DocumentFragment();
-					const select = App.UI.DOM.appendNewElement("select", el);
+
+					const options = [];
 					for (const slave of newSlaves) {
-						const option = App.UI.DOM.appendNewElement("option", select, `${SlaveFullName(slave)} (${slave.genes}, ${slave.actualAge})`);
-						option.value = slave.ID.toString();
+						options.push({
+							key: slave.ID.toString(),
+							name: `${SlaveFullName(slave)} (${slave.genes}, ${slave.actualAge})`
+						});
 					}
-					select.selectedIndex = -1;
-					select.onchange = () => {
-						const ID = Number.parseInt(select.options[select.selectedIndex].value);
-						const srcSlave = getSlave(ID);
+					const select = App.UI.DOM.makeSelect(options, null, slaveID => {
+						const srcSlave = getSlave(Number.parseInt(slaveID));
 						jQuery(linkDiv).empty().append(relativeLinkStrip(srcSlave, srcSlave));
-					};
+					});
 					App.UI.DOM.appendNewElement("div", el, App.UI.DOM.combineNodes(`Relative of slave: `, select));
+
 					const linkDiv = App.UI.DOM.appendNewElement("div", el, ``);
 					App.UI.DOM.appendNewElement("div", el, "Warning: related slaves will influence each others' opinion of you, and may become difficult to control if not properly broken.", "note");
 					App.UI.DOM.appendNewElement("div", el, App.UI.DOM.passageLink("Back", "Starting Girls"));
diff --git a/src/personalAssistant/assistantAppearance.js b/src/personalAssistant/assistantAppearance.js
index e713166e86c5b3cec0dcb9fc1afcef2b61a1bae4..27c9f3d2e606b204bc98d0dd94023a6a3bb61af2 100644
--- a/src/personalAssistant/assistantAppearance.js
+++ b/src/personalAssistant/assistantAppearance.js
@@ -1960,21 +1960,18 @@ globalThis.availableAssistantAppearances = function() {
 	const el = document.createElement("div");
 	const {hisA} = getPronouns(assistant.pronouns().main).appendSuffix('A');
 	el.append(`Select ${hisA} appearance: `);
-	const select = App.UI.DOM.appendNewElement("select", el);
+
+	const options = [];
 	for (const [appearance, obj] of App.Data.Assistant.appearances) {
 		if (obj.requirements) {
-			const option = App.UI.DOM.appendNewElement("option", select, capFirstChar(appearance));
-			option.value = appearance;
-			if (V.assistant.appearance === appearance) {
-				option.selected = true;
-			}
+			options.push({key: appearance, name: capFirstChar(appearance)});
 		}
 	}
-	select.onchange = () => {
-		const O = select.options[select.selectedIndex];
-		V.assistant.appearance = /** @type {assistantAppearance} */ (O.value);
+	el.append(App.UI.DOM.makeSelect(options, V.assistant.appearance, appearance => {
+		V.assistant.appearance =/** @type {assistantAppearance} */ (appearance);
 		App.UI.reload();
-	};
+	}));
+
 	return el;
 };
 
diff --git a/src/player/electiveSurgery.js b/src/player/electiveSurgery.js
index cbf10edda6d16cc2f851a948b245a282766ed020..55fd1e625c7099ed75a3063c5d7ed2106982131d 100644
--- a/src/player/electiveSurgery.js
+++ b/src/player/electiveSurgery.js
@@ -161,34 +161,22 @@ App.UI.electiveSurgery = function() {
 		App.Events.addNode(p, r, "div");
 
 		const choiceDiv = document.createElement("div");
-		const choices = new Map([]);
+		/**
+		 * @type {selectOption[]}
+		 */
+		const choices = [];
 		if (V.PC.skin !== V.PC.origSkin) {
-			choices.set(V.PC.origSkin, capFirstChar(`Restore natural ${V.PC.origSkin}`));
+			choices.push({key: V.PC.origSkin, name: capFirstChar(`Restore natural ${V.PC.origSkin}`)});
 		}
 		for (const skin of App.Medicine.Modification.naturalSkins) {
-			choices.set(skin, capFirstChar(skin));
+			choices.push({key: skin, name: capFirstChar(skin)});
 		}
 
-		const select = document.createElement("select");
-		let matchFound;
-		for (const [value, text] of choices) {
-			const option = document.createElement("option");
-			option.text = text;
-			option.value = value;
-			if (V.PC.skin === option.value) {
-				option.selected = true;
-				matchFound = true;
-			}
-			select.append(option);
-		}
-		if (!matchFound) {
-			select.selectedIndex = -1;
-		}
-		select.onchange = () => {
-			V.PC.skin = select.options[select.selectedIndex].value;
+		const select = App.UI.DOM.makeSelect(choices, V.PC.skin, value => {
+			V.PC.skin = value;
 			cashX(forceNeg(2000), "PCmedical");
 			showDegradation("skinTone");
-		};
+		});
 
 		choiceDiv.append(select, " or custom ", App.UI.DOM.makeTextBox(
 			V.PC.skin, v => {