Split a HTML tag with contents at caret position into multiple tags

Viewed 42

I am facing some problems splitting tag contents at caret position. I can get the spliting index as well as the contents of the tag at caret position after spliting. The problem I am facing is shown below.

<html>
 <div contenteditable="true">
  <strong><em> Suppose this is the content inside tag </em></strong>
 </div>
</html>

i have set the cursor between the word "content" and "inside". If i press a button now it should do something like below:

<html>
 <div contenteditable="true">
  <strong>
   <em> Suppose this is the content </em>
  </strong>
  """the cursor will be here""" 
  <strong>
   <em>inside tag </em>
  </strong>
 </div>
</html>
1 Answers

First get the selection, find the cursor position, locate it with in the editable div, and split the innerHTML at that position.

Next, starting with the target element, (the innermost element that was clicked, not necessarily the same as the element the click handler was added to), use a do {} while () loop and the parentNode property, to create an array of nested tags.

Convert the array of nested tags into a string, first, closing tags, then second, open tags. The array is iterated twice, once with the initial order, then a second time after reversing the order.

Having the "before" HTML, the tag close then open string, and the "after" HTML, these are then combined and finally the editable div is updated.

document.querySelector("div").addEventListener("click", function({target, currentTarget}) {
  if (target !== currentTarget) {
    let sel = window.getSelection(),
      elem = target, // target is clicked element
      ao = sel.anchorOffset, // get cursor postion in target
      an = sel.anchorNode,
      txt = an.textContent,
      ctxt = currentTarget.innerHTML, // currentTarget is event listener element
      cao = ctxt.indexOf(txt) + ao, // get cursor position in currentTarget
      bef = ctxt.substring(0, cao),
      aft = ctxt.substring(cao, ctxt.length),
      tags = [];

    // get all nested tags
    do {
      tags.push(elem.tagName);
      elem = (undefined === elem) ?
        target.parentNode : elem.parentNode;
    } while (elem !== currentTarget);

    // build new HTML with close and open tags inserted at cursor
    let ntxt = bef; // start with HTML before cursor
    // convert tag array to string of close tags
    ntxt += tags.reduce((prev, tag) => prev + '</' + tag + ">", "");

    // reverse tag array
    ntxt += tags.reverse() 
      // then convert to open tags
      .reduce((prev, tag) => prev + '<' + tag + ">", "");

    // end with HTML after cursor
    ntxt += aft;
    
    currentTarget.innerHTML = ntxt;    
    sel.collapse(sel.focusNode, 2); // frankly, I don't know why "2" works here :S
    
    // resulting (encoded) HTML contents of div
    console.log(currentTarget.innerHTML);
  }
});
<div contenteditable="true">
  <strong><em> Suppose this <span>is the</span> content inside tag </em></strong>
</div>

Related