Updated #2: A combination of things seemed to work in this example.
It took first listening to the container for the 'Delete' keydown event and also listening to the contenteditable="false"
span for its accidental deletion from the container.
Inpsired by this post on cursor position and this post on manually dispatching keyboard events
const DOMNodeRemoved = 'DOMNodeRemoved';
const DELETE = 'Delete';
const container = document.querySelector(".container");
container.addEventListener('keydown', containerListner, false);
const unEditableSelector = '[contenteditable="false"]';
const unEditable = container.querySelector(unEditableSelector);
unEditable.addEventListener("DOMNodeRemoved", unEditableListner, false)
function containerListner(e) { return e.key === DELETE && manuallyTriggerDeleteKey(e) }
function unEditableListner(e) { return e.type === DOMNodeRemoved && container.append(unEditable) }
function manuallyTriggerDeleteKey(e) {
const cursorPosition = window.getSelection().getRangeAt(0).startOffset
const stringToDelete = e.target.textContent.substr(cursorPosition).trim();
const stripped = unEditable.textContent.trim();
if (stripped === stringToDelete ) { return e.preventDefault(); }
return new KeyboardEvent('keypress', { 'key': 'Delete' })
}
<div class="container" contenteditable="true">
This Content Should be Editable
<span contenteditable="false"
style="display: inline;">
This Should not be Editable</span>
</div>
You could try to take advantage of the focus
and blur
events and selectively hide and show manage the span.
here's an example
Updated 1: The issue is that the span could get deleted if the user does something like uses delete key instead of backspace
With a DOMNodeRemoved event listener, you could append the node back to the container in case of accidental deletion.
here's an example inspired by this post on DOM mutation events
const DOMNodeRemoved = 'DOMNodeRemoved';
let unEditable = null;
const unEditableSelector = '*[contenteditable="false"]';
const container = document.querySelector(".container");
const appendUneditable = (evt) => unEditable &&
!evt.relatedNode.querySelector(unEditableSelector) &&
evt.relatedNode.append(unEditable);
const listener = (evt) => evt.type === DOMNodeRemoved && appendUneditable(evt);
container.addEventListener("focus", focus);
container.addEventListener("blur", blur);
function focus(e) {
unEditable = e.target.querySelector(unEditableSelector);
unEditable.addEventListener("DOMNodeRemoved", listener, false);
}
function blur(e) {
unEditable && e.target.append(unEditable);
unEditable && unEditable.removeEventListener("DOMNodeRemoved", listener);
}