BJSpell and contenteditable div - Trying to position the error marks

50 Views Asked by At

I'm creating a basic spellchecker that uses the BJSpell.js library.

I've found I cannot reliably update the contenteditable div directly without losing focus / resetting the caret to the beginning. I've tried many ways to set the cursor back to where it was pre-check, but nothing works.

So, my current method is to apply a transparent element over the div, then set the marking in that. This works, but the problem is the marks do not move with the text. For example, if I write 'colour' then it fails the check against a US dictionary, which is what I want. It marks the word in my hovering element at the position of the word. Also fine.

But if I then move that word, either by adding text before it or putting it onto a new line with Enter, the error mark stays floating where is originally was. I don't know how to move the mark to the position the word is now in.

Here is the code so far:

var dictionary = 'https://rawcdn.githack.com/maheshmurag/bjspell/master/dictionary.js/en_US.js';
var lang = BJSpell(dictionary, function() {
  //
});

const containsBadWord = word => {
  const found = lang.check(word);
  return found;
}

const processSpellCheck = text => {
  const allWords = text.split(/\b/);
  const newContent = allWords.map((word, index) => {
    word = word.replace(/[0-9]/g, '');
    word = word.replace(/[^\w\s]|_/g, '');
    var text = word;
    if (!containsBadWord(word.toLowerCase())) {
      text = $("<span />")
        .addClass("word-error")
        .text(word);
    }
    return text;
  });
  return newContent;
}

function initalizeSpellcheck(editorRef) {
  var editorSize = editorRef.getBoundingClientRect();

  var spellcheckContainer = $("<div />", {})
    .addClass("word-check")
    .prop("spellcheck", "false");

  var spellcheckSpan = $("<div />")
    .addClass("word-check-text-content")
    .css({
      width: editorSize.width,
      height: editorSize.height,
      position: "absolute",
      zIndex: -1
    });

  spellcheckContainer.append(spellcheckSpan);
  spellcheckContainer.insertBefore(editorRef);

  $(editorRef).on("keyup.spellcheck", function(event) {
    if (event.keyCode == 32 || event.keyCode == 13) {
      var newText = $(event.target).text();
      var newContent = processSpellCheck(newText);
      $(".word-check .word-check-text-content").text("");
      $(".word-check .word-check-text-content").append(newContent);
    }
  });
}

$(document).ready(function() {
  var editor = document.querySelector("#my_editing_div");
  initalizeSpellcheck(editor);
});
.word-check {
  color: transparent;
}

.word-error {
  border-bottom: 3px solid orange;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script type="text/javascript" src="https://rawcdn.githack.com/maheshmurag/bjspell/master/BJSpell.js"></script>
Test my wordz
<div id='my_editing_div' contenteditable='true' spellcheck='false' style='width:500px;height:300px;border:1px solid black;'></div>

If the code snippet isn't loading, there's a JSFiddle here.

If you enter some text with a non-US word that word highlights. But try moving the position of that word, or hitting Enter a few times then type again, and you'll soon see the problem.

How do I fix this positioning issue?

EDIT: I know I need to pass the HTML of the proper div over to the hidden div to ensure the elements are all in the correct place, then iterate through the hidden div to mark the bad words, but how do I achieve that?

0

There are 0 best solutions below