Search code examples
javascripthtmlrangeselectionkeyboard-input

insert a span in a contenteditable, and then user can not continue input after and out the span that just be insert


  • the code target : when user press space, auto input a span, and then user can continue input,
  • when i trace them in the devtools,range.setEnd(lasttextnode, 0); and range.collapse() run in right way,
  • but the keyboard input is not right, i set the caret on the text, want to insert char just after the span, but input appear in the previous span that just be insert.

here is my code :

editor.onkeydown = editinput;
var sel = window.getSelection();

function editinput(e) {
  var range = sel.getRangeAt(0);

  if (e.isComposing || e.keyCode === 229) { //imk
    return;
  }
  if (e.keyCode == 32) { //space

    var tnode = range.commonAncestorContainer;
    var start = range.startOffset;
    var text = tnode.textContent;

    var span = document.createElement('span');
    span.innerHTML = '------span------';

    var firsttextnode = document.createTextNode(text.substring(0, start));
    var lasttextnode = document.createTextNode(text.substring(start));
    var fragment = new DocumentFragment();
    fragment.append(firsttextnode, span, lasttextnode);
    tnode.replaceWith(fragment);
    range.setEnd(lasttextnode, 0); // here is problem,
    range.collapse();

    e.stopPropagation(); //bubble
    e.preventDefault(); //default.
  }
}
[contenteditable=true] {
  display: inline-block;
  white-space: pre-wrap;
  width: 400px;
  height: 100%;   
}

div, textarea,   span {
  border: 1px solid rgba(122, 206, 143, 0.966);
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
<div id="editor" contenteditable="true" class="Weave" tabIndex="1">
  samelinesamelinesamelinesamelinesamelinesamelinesame
  <span>deerge span</span>oaeueouaoeu <span>disange span</span>
</div>

you can see: the keyboard input will in span that just insert.

but if i change range.setEnd(lasttextnode, 0); to range.setEnd(lasttextnode, 1), it will run right;

i have try a lot of method:

  1. range.collapse();
  2. range.setStart(lasttextnode,0)
  3. sel.removeAllRanges();
  4. sel.collapse(lasttextnode,0)
  5. range.setStartBefore(lasttextnode);
  6. range.setStartAfter(span);

all of them is not work. just insert a space before the lasttextnode can solve the question partly, but that is not my target, i want input something after the span closely.


Solution

  • You can try inserting an empty character (e.g. \u200B) in between the span and the last text node. Then when you move cursor to right before the last text node, it's outside the span tag. But remember to erase the empty character from the result before inserting it into database.

    editor.onkeydown = editinput;
    var sel = window.getSelection();
    
    function editinput(e) {
      var range = sel.getRangeAt(0);
    
      if (e.isComposing || e.keyCode === 229) { //imk
        return;
      }
      if (e.keyCode == 32) { //space
    
        var tnode = range.commonAncestorContainer;
        var start = range.startOffset;
        var text = tnode.textContent;
    
        var span = document.createElement('span');
        span.innerHTML = '------span------';
        var emptyNode = document.createTextNode('\u200B');
    
        var firsttextnode = document.createTextNode(text.substring(0, start));
        var lasttextnode = document.createTextNode(text.substring(start));
        var fragment = new DocumentFragment();
        fragment.append(firsttextnode, span, emptyNode, lasttextnode);
        tnode.replaceWith(fragment);
        range.setEnd(lasttextnode, 0); // here is problem,
        range.collapse();
    
        e.stopPropagation(); //bubble
        e.preventDefault(); //default.
      }
    }
    [contenteditable=true] {
      display: inline-block;
      white-space: pre-wrap;
      width: 400px;
      height: 100%;   
    }
    
    div, textarea,   span {
      border: 1px solid rgba(122, 206, 143, 0.966);
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    <div id="editor" contenteditable="true" class="Weave" tabIndex="1">
      samelinesamelinesamelinesamelinesamelinesamelinesame
      <span>deerge span</span>oaeueouaoeu <span>disange span</span>
    </div>