Datalist w/ ContentEditable
by Kyle Mitofsky
HTML
<h2>Regular - Input + Datalist</h2>
<label>Choose a browser from this list:
<input type="text" list="browsers" placeholder="Edit Me" id="regular" /></label>
<datalist id="browsers">
<option value="Chrome"/>
<option value="Firefox"/>
<option value="Internet Explorer"/>
<option value="Opera"/>
<option value="Safari"/>
<option value="Microsoft Edge"/>
</datalist>
<h2>Workaround - ContentEditable + Datalist</h2>
<label>Choose a browser from this list:
<span contenteditable list="browsers" name="myBrowser">edit me</span></label>
<div style='position:fixed;bottom:0;left:0;background:lightgray;width:100%;padding:3px;'>
About this question on SO
<a href='https://stackoverflow.com/a/61399491/1366033'>
html datalist for contenteditable (non-input tag)
</a>
</div>
CSS
[contenteditable] {
position: relative;
border: 1px solid silver;
padding: 2px 5px;
}
.editable-mirror {
position: absolute;
left: -1px;
top: -1px;
height: calc(100% + 2px);
width: calc(100% + 12px);
padding: 2px 5px;
margin: 0;
border: 0;
font: inherit;
}
/* demo styles */
#regular {
width: 100px;
}
JavaScript
// todo: handle shift + tab
document.addEventListener('focusin', function (event) {
if (event.target.matches('[contenteditable]')) {
console.log('focused')
var editable = event.target
// enter edit mode
editable.classList.add("editing")
// get text
var text = editable.innerText
// create input
var input = document.createElement("input");
input.type = "text";
input.className = "editable-mirror";
input.setAttribute("list", "browsers");
input.value = text;
editable.appendChild(input);
input.focus()
}
}, false);
document.addEventListener('focusout', function (event) {
if (event.target.matches('.editable-mirror')) {
console.log('blur')
var input = event.target
var editable = input.closest("[contenteditable]")
// leave edit mode
editable.classList.remove("editing")
// get text
var text = input.value;
// destroy input
input.parentNode.removeChild(input);
// apply value
editable.innerText = text;
}
}, false);