3

I have a div tag with contenteditable set to true. I want the user to be able to edit the text inside that div, but not remove any additional div or span tags inside. For example with the following code.

<div class="container" contenteditable="true">
This Content Should be Editable
<span contenteditable="false"
   style="display: inline;">
   This Should not be Editable</span>
</div>

I have the desired result in the span tag when I set its contenteditable to false. But when i press backspace in the div tag i am also able to remove the whole span tag. I want to lock the span tag in the div, like dont allow the user to remove it or edit it. For now I think having a contenteditable div tag is allowing the user to remove the span completely. Is there a work around to achieve this. And I donot want to have the editable text in another span.

j08691
  • 204,283
  • 31
  • 260
  • 272
bna-it
  • 71
  • 1
  • 8
  • the latest update #2 seems to be the best I can get away with without using form field change events or modifying your original code – 4UmNinja Jul 30 '20 at 05:25

2 Answers2

1

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;">
   &nbsp;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);
}
4UmNinja
  • 510
  • 4
  • 14
  • This still deletes the span if you press backspace all the way. and its hiding the span, which is not what i am looking for. – bna-it Jul 28 '20 at 04:19
  • 1
    you could just append the span back to the div if not found .. it's a small stretch from this example. might update if I have more time **Updated** – 4UmNinja Jul 28 '20 at 18:57
0

i suggest <pre contenteditable="plaintext-only"></pre this may help? (i know its outdated question but i have a answer.)