8

Short version:

I have a contenteditable div. I want to remove the last x chars before carret postion.

Long version:

If someone types a [ in the contenteditable div there should appear an autosuggest list, filled by an ajax call. This is allready working. I can also insert the selected suggestion at carret position and add a ]. But the chars, already typed by the user, should be deleted before appending the suggestion. The bracket [ is not only the "trigger-char" but also a markdown-like format element.

Example:

Text in the div ("|" stands for carret): Lorem ipsum| sit lorem ipsum dolor

User now types [do to start auto suggestion: Lorem ipsum [do| sit lorem ipsum dolor

dolor is suggested and inserted after carret: Lorem ipsum [do|dolor sit lorem ipsum dolor

Problem: the allready typed chars do should be removed before inserting the suggestion

Desired behaviour: Lorem ipsum [dolor] sit lorem ipsum dolor

Attempted Solutions:

Insert text at cursor in a content editable div - working but i can't remove the last chars https://developer.mozilla.org/en-US/docs/Web/API/Selection - tried to get some ideas from MDN, but no idea how to change the selection's textNode's content

Code:

Extract the string between last "[" and carret to search for suggestions in DB:

var sel = window.getSelection();
var cords = getCaretCoords();
var searchQuery = extractSearchQuery(sel.anchorNode.data, sel.anchorOffset, "[");

function extractSearchQuery(searchString, caretPos, triggerString) {
    //cut everything after carret
    searchString = searchString.slice(searchString.lastIndexOf(triggerString) + 1, caretPos);
    return searchString;
}

Insert the suggestion after carret:

function insertTextAtCursor(text) {
    var sel, range, html;
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.deleteContents();
            var newTextNode = document.createTextNode(text);
            range.insertNode(newTextNode);
        }
    } else if (document.selection && document.selection.createRange) {
        document.selection.createRange().text = text;
    }
}
superjojo140
  • 101
  • 7
  • 1
    Welcome to SO. Please include attempted solutions, why they didn't work, and the expected results. That would really helps us to figure out the issue with your code. Thanks! – palaѕн Dec 19 '17 at 12:28
  • For my understanding, if someone types `ips`, and then the "[", you want to autosuggest `ipsum` and then replace `ips[` by `ipsum`? If so, you apparently already know that you have to look for something starting with `ips`, so you can just string replace `ips[` by `ipsum`, right? So something like `divtext.replace(lookup + "[", found);`, where `divtext` is the content of your editable div, `lookup` is the lookup string so `ips` in this example and `found` is the string found by the autosuggest feature, `ipsum` in this case. – rolfv1 Dec 19 '17 at 12:37
  • Please add your current code into the question. I doubt anyone implement this from 0 for you. – Lajos Arpad Dec 19 '17 at 12:51
  • @rolfv1 see updated example. Simple `replace` doesn't work, because the suggested words can occur several times in the text – superjojo140 Dec 19 '17 at 12:57
  • 1
    @superjojo140 you could store the index of text where this happens into a variable and then override the relevant chunk, knowing the starting index and length. But we still need your current code to help you... – Lajos Arpad Dec 19 '17 at 13:01
  • @LajosArpad see edited question – superjojo140 Dec 19 '17 at 13:21

3 Answers3

2

With the following assumptions and definitions:

  • You know the location of the "[" as this triggers the auto-suggest to start working after all. Let's call this indexbracket
  • You know what text has been typed after the bracket as you used that for the auto-suggestion. Let's call this lookupstring

Then you should replace the content of your div by:

divtext = divtext.substring(0, indexbracket) + divtext.substring(indexbracket + lookupstring.length + 1);

Example: (in which I used different methods to find indexbracket etc. than you probably used)

var divtext = "Lorum ipsum [doAUTOSUGGESTEDTEXT sit";
var indexbracket = divtext.indexOf('[');
var lookupstring = "do";
divtext = divtext.substring(0, indexbracket) + divtext.substring(indexbracket+lookupstring.length+1);
console.log(divtext);

Update after edit of post Given your need for the position of the caret, you could opt to "manually" reset the location of the caret after replacing the text, see this excellent answer here: https://stackoverflow.com/a/512542/3000771

rolfv1
  • 571
  • 3
  • 14
  • Thanks a lot for this idea! Only problem: there can be serveral brackets in the text. The bracket `[` is and should not be removed by auto suggestion. How to find the last `[` before carret? – superjojo140 Dec 19 '17 at 13:29
  • I know, that was just for this example, you have your own methods to find the location of the "right" bracket, as you do here in your own code: `searchString = searchString.slice(searchString.lastIndexOf(triggerString) + 1, caretPos);`. The `lastInxedOf`-part is what I call `indexbracket`. Just make sure that you store that index in a global variable and you are good to go I'd say. – rolfv1 Dec 19 '17 at 13:32
1

You are already using window.getSelection(), which is correct. Using window.getSelection().baseOffset gives you the index you are at. From here, provided that you know where your selection starts in comparison to the starting position of the chunk to replace, you will be able to compute its starting index. You know the length as well, so you will need to get the starting substring and the ending substring of your content (omit the chunk to be replaced) and concatenate between them the new content.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
1

Thanks for all advices.

My solution is now working similiar to that answer: https://stackoverflow.com/a/11248187/8622487

var length = lookupstring.length; //length of the allready typed string
var sel = window.getSelection();
sel.collapseToStart();
for (var i = 0; i < length; i++) {
    sel.modify("move", "backward", "character");
}
for (var i = 0; i < length; i++) {
    sel.modify("extend", "forward", "character");
}

This selects the allready typed chars. When inserting the matching keyword this is overwritten.

I know this is quick and dirty, but it works (in Firefox and Chrome)

superjojo140
  • 101
  • 7