Last active
August 13, 2023 10:27
-
-
Save akingdom/8edbc92dccbe686d340221e28fb95abb to your computer and use it in GitHub Desktop.
Example of a basic editable HTML dropdown (SELECT menu), aimed at easy use on multiple SELECT elements.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<!-- | |
This replaces the SELECT dropdown menu with an editable INPUT textbox, | |
when the last ('other') option is selected from the list. | |
This version allows adding this functionality to multiple SELECT elements. | |
(See the 'setupEditableSelectElement' method.) | |
Note that this may not be a good fit when supplying both display values and raw values, | |
as evidenced by the 'spanish' option, for example. | |
Future: It wouldn't be that hard to add in functionality to switch back to dropdown menu... | |
Lookup the edited value against the original SELECT OPTIONS list (see 'allOptions') | |
and if a match is found, then transform the element from INPUT back to SELECT. | |
Future: A better way to do this might be to just swap the tag of the SELECT element to INPUT | |
and check the tag in the onChange | |
By Andrew Kingdom (C) 2023 all rights reserved. MIT License. | |
--> | |
<head> | |
<style> | |
.selecteditable { | |
width: 100%; | |
height: 30px; | |
border: 1px solid black; | |
} | |
input.selecteditable { | |
background-color: cornsilk; | |
} | |
option { | |
padding: 10px; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Hello</h1> | |
Dear people, | |
<select name="languages" id="languages" class="selecteditable"> | |
<option value="english" selected>English</option><!-- OK --> | |
<option value="spanish">Español</option><!-- OK --> | |
<option value="greek">Ελλενικα</option><!-- OK --> | |
<option>French</option><!-- should have a value - won't be found --> | |
<option>German</option><!-- should have a value - won't be found --> | |
<option value="other">Other</option> | |
</select> | |
This example was brought to you by someone you know. | |
</body> | |
<script type="text/javascript"> | |
document.addEventListener('DOMContentLoaded', function () { | |
// document is ready to query/use | |
console.log('The DOM is ready!'); | |
// Specify the SELECT element and the OPTION within it (if any), then prepare it for use. | |
var languagesSelect = document.getElementById('languages'); | |
var defaultValue = 'greek'; // stored value or undefined if nothing is stored | |
setupEditableSelectElement(languagesSelect, defaultValue); | |
function setupEditableSelectElement(selectElement, defaultValue) { | |
// parameters: | |
// selectElement = the SELECT element to modify | |
// defaultValue = the value to select | |
// When undefined or '', the first SELECTED OPTION is used, or the first OPTION if none are SELECTED. | |
// The OPTION value "other" is a special case that will replace the entire SELECT element with an *editable* INPUT text box when selected, | |
// for example, <option value="other">Enter your own value</option> | |
// | |
selectElement.dataset.priorSelectedValue = defaultValue; // dynamic state -- this is the only one we care about ongoing. | |
selectElement.dataset.defaultValue = defaultValue; // static state | |
selectElement.dataset.firstOption = selectElement.querySelector('option'); // static state | |
selectElement.dataset.firstSelectedOption = selectElement.querySelector('option[selected]'); // static state | |
//selectElement.dataset.allOptions= selectElement.querySelectorAll('option').join(','); // static state -- this could be useful to track if we switch to *edit mode*. | |
// Set the initially selected option... | |
var validDefaultValue; | |
if (typeof defaultValue === 'undefined' || defaultValue === '') { | |
// defaultValue has no value | |
validDefaultValue = false; | |
} else { | |
// defaultValue has a value, check it exists | |
if (selectElement.querySelectorAll(`option[value="${selectElement.dataset.defaultValue}"]`).length != 0) { | |
// defaultValue has a value that exists | |
validDefaultValue = true; | |
} else { | |
// defaultValue has a value that does not exist | |
validDefaultValue = false; | |
} | |
} | |
// set the previous value for later use | |
if (validDefaultValue) { | |
selectElement.dataset.priorSelectedValue = selectElement.dataset.defaultValue; | |
} else { | |
// set a valid previous value, which will be the value to edit, since the specified one doesn't exist | |
if (typeof defaultValue !== 'undefined' && defaultValue !== '') { | |
selectElement.dataset.priorSelectedValue = defaultValue; // defaultValue specified but doesn't exist | |
} else if (selectElement.dataset.firstSelectedOption) { | |
selectElement.dataset.priorSelectedValue = selectElement.dataset.firstSelectedOption.value; // defaultValue not specified, use first selected item in HTML list | |
} else if (firstOption) { | |
selectElement.dataset.priorSelectedValue = selectElement.dataset.firstOption.value; // defaultValue not specified, use first item in HTML list | |
} else { | |
return; // no options -- don't continue setting this up | |
} | |
selectElement.dataset.defaultValue = 'other'; // trigger edit mode, since we don't have a match so it must be a custom value | |
} | |
console.log('initialSelectedValue: ' + selectElement.dataset.defaultValue); | |
// Show options when clicked/tapped. | |
selectElement.addEventListener('click', function () { | |
this.classList.add('open'); | |
}); | |
const handleChange = function (currentElement) { | |
if (currentElement.value === 'other') { | |
const newElement = document.createElement('input'); | |
newElement.name = currentElement.name; | |
newElement.id = currentElement.id; | |
newElement.dataset.priorSelectedValue = selectElement.dataset.priorSelectedValue; | |
newElement.dataset.defaultValue = selectElement.dataset.defaultValue; | |
newElement.dataset.firstOption = selectElement.dataset.firstOption; | |
newElement.dataset.firstSelectedOption = selectElement.dataset.firstSelectedOption; | |
//newElement.dataset.allOptions = selectElement.dataset.allOptions; | |
newElement.value = currentElement.dataset.priorSelectedValue; // the value to edit -- it may be preferred to do... newElement.value = ''; | |
newElement.classList = currentElement.classList; | |
newElement.addEventListener('DOMNodeInserted', function (event) { | |
if (event.target === newElement) { | |
console.log('The local DOM is updated!'); | |
// after the element is added | |
newElement.focus(); | |
newElement.select(); | |
} | |
}); | |
currentElement.parentNode.replaceChild(newElement, currentElement); | |
} else { | |
// remember the new selection as the value to edit | |
currentElement.dataset.priorSelectedValue = currentElement.options[currentElement.selectedIndex].value; | |
} | |
}; | |
selectElement.addEventListener('change', (event) => handleChange(event.target)); | |
var initialOption = selectElement.querySelectorAll(`option[value="${selectElement.dataset.defaultValue}"]`); | |
if (initialOption.length != 0) { | |
initialOption[0].selected = true; | |
console.log(`selected: "${selectElement.dataset.defaultValue}"`); | |
handleChange(selectElement); | |
console.log(`selected2: "${selectElement.dataset.defaultValue}"`); | |
} | |
} | |
}); | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment