The title is a bit misleading maybe; my spellchecker focuses more on format than spelling (caps, punctuation and spaces, apostrophes, converting internet slang to full words, oft-scrambled words etc.). However the basic principles apply.
Basically, the JS/jQuery checker I'm building would correct words as they are typed (after a space or punctuation has been typed after the word).
However, much like any autocorrecting, it's bound to run into mistakes. I'm not even considering creating functionality that would determine whether "its" or "it's" is more appropriate in a given case (though if such a plugin or code snippet exists, do point me to one).
So I want to make it a "yielding" autocorrect (for the lack of the knowledge of a better name). Basically;
- User types in a word that would set off the checker, and types a space.
- The checker corrects the word.
- The user deems this a mistake and corrects it back (by Backspacing the whole word, or parts of it, or highlighting it or however they feel comfortable editing it).
- The user continues typing, and the checker doesn't touch that instance of that word again.
Now easiest of course would be to disable the check for that word entirely, but I want the checker to correct future instances of it. What I'm looking for would detect a user editing an autocorrected word (regardless whether right after typing or later) back to what it was before being autocorrected, and then learning to leave that specific instance of that word alone.
I don't even know where to begin with this. I'm thinking a contenteditable with each word wrapped in a span, autocorrected ones having a special class and a data-* attribute containing the original one, listen for edits on the autocorrected words, and if it's edited back to equaling the data-* value, add a class that leaves it out of future autocorrect rounds.
I'm thinking though that this might be unnecessarily complicated, or at least not the path of least resistance. What would be the smartest way of doing this?
Your suggested approach (separating each word in a
span
and storing additional data in it) at first glance seems to be the most sensible approach. On the editor level, you just need to ensure all text is inside somespan
, and that each of them contains only a single word (splitting it if necessary). On the word level, just listen for changes in thespan
s (bindinginput
andpropertyChange
) and act according to its class/data.However, the real pain is to keep the caret position consistent. When you change the contents of either a
textarea
or an element withcontentEditable
, the caret moves rather unpredictably, and there's no easy (cross-browser) way of keeping track of the caret. I searched for solutions both here at SO and elsewhere, and the simplest working solution I found was this blog post. Unfortunatly it only applied totextarea
, so the "each word in a span" solution couldn't be used.So, I suggest the following approach:
Array
, where each word stores both the current value and the original;textarea
changes, keep the set of unchanged words and redo the rest;backspace
;backspace
once will undo it, and it won't be checked again unless modified.backspace
will undo one correction until no one is left.backspace
once to "protect" it.I created a simple proof-of-concept at jsFiddle. Details below. Note that you can combine it with other approaches (for instance, detecting a "down arrow" key and displaying a menu with some auto-correcting options) etc.
Steps of the proof-of-concept explained in detail:
Keep a list of words in an
Array
, where each word stores both the current value and the original;This regex splits the text into words (each word has a
word
property and asp
one; the latter stores non-word characters immediatly following it)When the contents of the
textarea
changes, keep the set of unchanged words and redo the rest;Only apply the spell check if the caret is just after a non-word character (room for improvement) and you're not hitting
backspace
;If the user was unsatisfied with the correction, hitting
backspace
once will undo it, and it won't be checked again unless modified.The code for
updateText
simply joins all words again into a string, and set the value back to thetextarea
. The caret is preserved if nothing was changed, or placed just after the last autocorrection done/undone, to account for changes in the text length:The final plugin structure:
Assuming you're only submitting the word left of the caret, could you disable the spellchecker until a whitespace character is typed or the textbox caret is moved?
I'm not sure if that's the kind of answer you wanted.