Home > front end >  Create Span on Each New Line in ContentEditable Div
Create Span on Each New Line in ContentEditable Div

Time:01-14

I have one div which has contenteditable property true. I want whenever new line is entered then that should be inside a span tag and that span should be inside p tag but whenever i am clicking on enter it just create new div.

Initial

<div contenteditable="true"></div>

Entering this text

  1. some text
  2. some new text
  3. some more new text

What i am getting

<div contenteditable="true">
    some text
    <div>some new text</div>
    <div>some more new text</div>
</div>

What i want

<div contenteditable="true">
    <p><span>some text</span></p>
    <p><span>some new text</span></p>
    <p><span>some more new text</span></p>
</div>

How can i achieve above pattern using javascript.

CodePudding user response:

Is there a need for the div to be initialized empty?

If not. You can already leave the initialized div with content with the formatting tags the way you want. Ex:

div {
  width: 150px;
  height: 150px;
  border: 1px solid red;
}
<div contenteditable="true">
  <p><span>Some Text</span></p>
</div>

So each time a user inserts a newline, it will be with the formatting and content tags already present in the div.

CodePudding user response:

You can do that by listening for the insertion of a line break and replacing it with the required elements, here is an example:

const elm = document.querySelector('#ce');
elm.addEventListener('beforeinput', (event) => {
  if (['insertLineBreak', 'insertParagraph'].includes(event.inputType)) {
    event.preventDefault();
    event.stopPropagation();

    const paragraph = document.createElement('p');
    const span = document.createElement('span');
    span.innerHTML = '&#8203;';
    paragraph.append(span);
    elm.append(paragraph);
    
    const range = new Range();
    range.selectNode(span);
    range.setStart(span, 0);
    range.collapse(true);

    const selection = document.getSelection();
    if (selection.rangeCount > 0) {
      selection.removeAllRanges();
    }
    selection.addRange(range);
  }
});
#ce {
  width: 200px;
  height: 100px;
  border: 1px solid black;
}
<div id="ce" contenteditable="true"></div>

Here, I'm using the beforeinput listener to check when a new line is inserted, I'm preventing that event event.preventDefault() and then, I'm creating the required elements (a <span> inside a <p>) and I'm using the Selection API to set the caret inside the newly created <span>.

The only unusual thing here is this:

span.innerHTML = '&#8203;';

I'm using this because the caret will not be positioned correctly if the <span> doesn't have any content. &#8203; is a zero width space character.

There are probably better ways of setting the caret's position.

  • Related