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);