bna-it
bna-it

Reputation: 91

Not Removing Child elements in a div with contenteditable set to true

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.

Upvotes: 5

Views: 1309

Answers (2)

CatCoded Beginner
CatCoded Beginner

Reputation: 1

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

Upvotes: 0

4UmNinja
4UmNinja

Reputation: 540

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

Upvotes: 3

Related Questions