2

So I'm obviously inserting elements in an editable div under selection. The problem might come when some element was already wrapped in a container. Next selection could include a part of it so, while trying to wrap the selected part again, there's a possibility of error

DOMException: Failed to execute 'surroundContents' on 'Range': The Range has partially selected a non-Text node.

So I'm trying to get innerHTML under the selection so that I could split eventual "edge" elements but can't find any way to dig it out so far.

Just in case the relevant code part is in snippet at the bottom.

If there's some way to get to that innerHTML, or alternatively find some alternative approach to this, I'd appreciate any help.

ie. Editable div content before anything

    <div contentEditable="true">
        phrase1 phrase2 phrase
    </div>

Editable div content after one selection of "e1 phras" part

    <div contentEditable="true">
        phras<span>e1 phras</span>e2 phrase
    </div>

Editable div content after another selection of "phrase1" part with splitted "edges" so that the p tag doesn't contain beginning of span while not containing its closing tag and throwing the error.

    <div contentEditable="true">
        <p>phras</p><span><p>e1</p> phras</span>e2 phrase
    </div>

document.getElementById('edit').addEventListener('mouseup', () => {
obj.saveSelection()});

document.getElementById('setItalic').addEventListener('mouseup', () => {obj.setItalic()});

document.getElementById('setBold').addEventListener('mouseup', () => {obj.setBold()});

const obj = {
  currentSelection: null,
  selectionRange: null,

  restoreSelection: function() {
    const selection = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(this.selectionRange);
    this.currentSelection = selection;
  },

  saveSelection: function() {
    const selection = document.getSelection();
    this.selectionRange = selection.rangeCount === 0 ? null : selection.getRangeAt(0);
  },

  surroundText: function(elem) { // Some DOM element. <a> <span> etc.
    this.restoreSelection();
    const range = this.currentSelection.getRangeAt(0);

    try{
      range.surroundContents(elem);
    }
    catch (e) {
      console.error(e); // DOMException: Failed to execute 'surroundContents' on 'Range': 
                        // The Range has partially selected a non-Text node.
    }
  },
  
  setBold: function() {
    this.surroundText(document.createElement('b'));
  },

  setItalic: function() {
    this.surroundText(document.createElement('i'));
  }
}
#edit {
  height: 200px;
  width: 200px;
  background-color: #444;
  color: white;
}
<button id="setItalic">italic</button>
<button id="setBold">bold</button>

<div id='edit' contentEditable="true"/>
Hekmatyar
  • 335
  • 2
  • 10
  • @ggorlen sure, already edited the question – Hekmatyar Jul 28 '21 at 01:52
  • 1
    Yup, it's here at the bottom – Hekmatyar Jul 28 '21 at 02:10
  • In your snippet div does not have any text – Alireza Ahmadi Jul 28 '21 at 02:52
  • And I don't understand this: `phrase1 phrase2 phrase` you have two buttons: italic and bold. where is button that surrounds text by span? – Alireza Ahmadi Jul 28 '21 at 02:55
  • 1
    It doesn't have any text. It's editable. It's meant to be filled with text by user. Try clicking it and typing something. I use "i" and "b" tags because changing the text with those is clearly visible and both of those are inline tags just like span. "p" tag wasn't a good example, sure, but well... it's "example" of how the wrapping should go. – Hekmatyar Jul 28 '21 at 03:06
  • 2
    See here: https://stackoverflow.com/questions/6328718/how-to-wrap-surround-highlighted-text-with-an-element – AbsoluteBeginner Jul 28 '21 at 09:17

0 Answers0