Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ContentEditable div - set cursor position after updating inner html

I have a spell check solution that uses a content editable div and inserts span tags around words that are misspelled. Every time the inner html of the div is updated, the cursor moves to the beginning of the div.

I know I can move the cursor to the end of the div if the user adds new words to the end of the sentence (code below).

Old Text: This is a spell checker|

New Text: This is a spell checker soluuution|

var range = document.createRange();
range.selectNodeContents(element[0]);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);

However, I am unable to retain the cursor position if the user adds words in the middle of a sentence.

Old Text: This is a spell checker

New Text: This is a new spell checker|

In the above case, the cursor goes to the end of the div when it should be after "new".

How do I retain the cursor position? Since I am updating the html and adding nodes, saving the range before the update and adding it to the selection object isn't working.

Thanks in advance.

like image 886
F.S. Avatar asked Apr 21 '17 04:04

F.S.


1 Answers

As far as I know, changing the content of the div will always have problem.

So here is the solution that I came with. Please type error word such as helloo, dudeee

This should ideally work for textarea as well.

Solution details:

  • Use a ghost div with same text content
  • Use transparent color for the ghost div
  • Use border-bottom for the ghost div span text
  • Change zIndex so that it does't appear infront

// some mock logic to identify spelling error
const errorWords = ["helloo", "dudeee"];

// Find words from string like '   Helloo   world .. '
// Perhaps you could find a better library you that does this logic.
const getWords = (data) =>{
  console.log("Input: ", data);
  const allWords = data.split(/\b/);
  console.log("Output: ", allWords)
  return allWords;
}

// Simple mock logic to identify errors. Now works only for 
// two words [ 'helloo', 'dudeee']
const containsSpellingError = word => {
  const found = errorWords.indexOf(word) !== -1;
  console.log("spell check:", word, found);
  return found;
}

const processSpellCheck = text => {
  const allWords = getWords(text);
  console.log("Words in the string: ", allWords);
  const newContent = allWords.map((word, index) => {
    var text = word;
    if(containsSpellingError(word.toLowerCase())) {
      console.log("Error word found", word);
      text = $("<span />")
        .addClass("spell-error")
        .text(word);
    }
    return text;
  });
  return newContent;
}

function initalizeSpellcheck(editorRef) {
  var editorSize = editorRef.getBoundingClientRect();
  
  var spellcheckContainer = $("<div />", {})
    .addClass("spell-check")
    .prop("spellcheck", "false");
 
  var spellcheckSpan = $("<span />")
    .addClass("spell-check-text-content")
    .css({
      width: editorSize.width,
      height: editorSize.height,
      position: "absolute",
      zIndex: -1
    });
    
  var text = $(editorRef).text();
  
  var newContent = processSpellCheck(text);
  spellcheckSpan.append(newContent);
  
  spellcheckContainer.append(spellcheckSpan);
  spellcheckContainer.insertBefore(editorRef);
  
  $(editorRef).on("input.spellcheck", function(event) {
      var newText = $(event.target).text();
      var newContent = processSpellCheck(newText); 
      $(".spell-check .spell-check-text-content").text("");
      $(".spell-check .spell-check-text-content").append(newContent);
  });
}


$(document).ready(function() {
  var editor = document.querySelector("#editor");
  initalizeSpellcheck(editor);
});
#editor {
  border: 1px solid black;
  height: 200px;
}

.spell-check {
  color: transparent;
}

.spell-error {
  border-bottom: 3px solid orange;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


<div id="editor" contenteditable="true" spellcheck="false">
dudeee
</div>
like image 58
Sivasankar Avatar answered Oct 04 '22 23:10

Sivasankar