1

  var input = document.getElementById("div_id");
  var username = "TEST";
  input.focus();

  var selection = window.getSelection();
  var range = selection.getRangeAt(0);

  var span = document.createElement('span');
  span.setAttribute('class', 'tagged-user');
  span.setAttribute('id', 55);
  span.innerHTML = username;

  //var initialText = input.textContent;
  //var before_caret = initialText.slice(0,getCaretPosition(input));
  // var after_caret = initialText.slice(getCaretPosition(input), initialText.length);
  // // var before_caret = input.textContent;
  // console.log("before_caret " + before_caret);
  // *******************************
  //this is the regex that match last @something before caret and it work good.
  //var droped_at = before_caret.replace(/@\w+(?!.*@\w+)/g, '');
  // *******************************
  
  // input.textContent = droped_at + after_caret;
  // console.log("before_caret " + before_caret);




  range.deleteContents();
  range.insertNode(span);

  range.collapse(false);
  range.insertNode(document.createTextNode("\u00a0"));
  // cursor at the last
  range.collapse(false);
  selection.removeAllRanges();
 selection.addRange(range);
.tagged-user {
  color: #1e81d6;
  font-weight: bold;
}
<div contenteditable="true" id="div_id">
First person <span class="tagged-user" id="1">@Joe </span> and @te and <span class="tagged-user"  id="2">@Emilie</span> want to go to the beach.
</div>

An example of a text: hello man, @te @emilie how are you?( @emilie is a span tag) In this example if the cursor is after @te, I want to replace @te by TEST.

Hello,

I am trying to make a facebook like mention system. I'm able to trigger a message that shows a list of all my contacts as soon as I type "@". Everything is working fine, the only problem is when I type "@te" for finding "TEST" in the list, it should be able to replace "@te" with "TEST". It inputs @teTEST, any clue how to do it ? I'm using a div with contenteditable.

Thanks a lot.

oalkaako
  • 13
  • 4
  • 1
    Please always create a snippet. It makes things much easier. And it helps us to help you! – Cristian S. May 24 '18 at 09:06
  • I agree with @CristianS. I'll be happy to help once you'll have added HTML (and maybe CSS) to create a working snippet! – Takit Isy May 24 '18 at 09:13
  • @CristianS. I modified the question into snippet, I want TEST to replace te when caret is after te. Thanks again. – oalkaako May 24 '18 at 09:54

1 Answers1

1

It seems to be sooo complicated to get/set caret position in a content-editable div element.

Anyway, I created a working snippet with the functions I found in these topics…
Get caret position in contentEditable div
Set Caret Position in 'contenteditable' div that has children
… and added a function oninput to make the replacement you wanted:
(See comments in my code for more details)

var input = document.getElementById("div_id");
var replaced = "@te";  // TAKIT: Added this variable
var replacer = "@TEST"; // TAKIT: Renamed this variable
var replacer_tags = ['<span class="tagged-user" id="0">', '</span>']; // TAKIT: Added after comment
input.focus();

// TAKIT: Added functions from these solutions:
// https://stackoverflow.com/questions/3972014/get-caret-position-in-contenteditable-div
function GetCaretPosition(element) {
    var caretOffset = 0;
    var doc = element.ownerDocument || element.document;
    var win = doc.defaultView || doc.parentWindow;
    var sel;
    if (typeof win.getSelection != "undefined") {
        sel = win.getSelection();
        if (sel.rangeCount > 0) {
            var range = win.getSelection().getRangeAt(0);
            var preCaretRange = range.cloneRange();
            preCaretRange.selectNodeContents(element);
            preCaretRange.setEnd(range.endContainer, range.endOffset);
            caretOffset = preCaretRange.toString().length;
        }
    } else if ( (sel = doc.selection) && sel.type != "Control") {
        var textRange = sel.createRange();
        var preCaretTextRange = doc.body.createTextRange();
        preCaretTextRange.moveToElementText(element);
        preCaretTextRange.setEndPoint("EndToEnd", textRange);
        caretOffset = preCaretTextRange.text.length;
    }
    return caretOffset;
}

// https://stackoverflow.com/questions/36869503/set-caret-position-in-contenteditable-div-that-has-children
function SetCaretPosition(el, pos) {
  // Loop through all child nodes
  for (var node of el.childNodes) {
    if (node.nodeType == 3) { // we have a text node
      if (node.length >= pos) {
        // finally add our range
        var range = document.createRange(),
          sel = window.getSelection();
        range.setStart(node, pos);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);
        return -1; // we are done
      } else {
        pos -= node.length;
      }
    } else {
      pos = SetCaretPosition(node, pos);
      if (pos == -1) {
        return -1; // no need to finish the for loop
      }
    }
  }
  return pos; // needed because of recursion stuff
}


// TAKIT: Added this whole function
input.oninput = function() {
  var caretPos = GetCaretPosition(input); // Gets caret position
  if (this.innerHTML.includes(replaced)) { // Filters the calling of the function
    this.innerHTML = this.innerHTML.replace(replaced, replacer_tags.join(replacer) + '&nbsp;'); // Replace
    caretPos += replacer.length - replaced.length + 1; // Offset due to strings length difference
    SetCaretPosition(input, caretPos); // Restores caret position
  }
  // console.clear(); console.log(this.innerHTML); // For tests
}
.tagged-user {
  color: #1e81d6;
  font-weight: bold;
}
<div contenteditable="true" id="div_id">
  First person <span class="tagged-user" id="1">@Joe</span> and (add "te" to test) @ and <span class="tagged-user" id="2">@Emilie</span> want to go to the beach.
</div>

Hope it helps.

Takit Isy
  • 9,688
  • 3
  • 23
  • 47
  • Just to make sure we are aligned on the test scenario: take the div totally empty, type in `@te`then type space then type in `some charcaters` => do you get `@test`in blue then `some characters`in regular charcaters outside the div ? Cause I get the hole within the div in blue. Again, many thanks for your help – oalkaako May 24 '18 at 17:10
  • @oalkaako I've updated: I changed the space character for a non-breaking space, because the regular space is removed when at the end of the line. – Takit Isy May 24 '18 at 18:34