标题可能有点误导;我的拼写检查器更多地关注格式而不是拼写(大写,标点符号和空格,撇号,将互联网俚语转换为完整单词,经常加扰的单词等)。但是基本原则适用。
基本上,我正在构建的JS / jQuery检查器会在键入单词时更正单词(在单词后面输入空格或标点符号后)。
然而,就像任何自动修正一样,它必然会遇到错误。我甚至没有考虑创建能够确定“它的”或“它的”在某种情况下是否更合适的功能(尽管如果存在这样的插件或代码片段,请指出一个)。
所以我想让它成为一个“让步”自动更正(因为缺乏对更好名字的了解)。基本上;
现在最简单的方法是完全禁用对该单词的检查,但我希望检查程序能够更正它的未来实例。我正在寻找的是检测用户编辑自动更正的单词(无论是在打字后还是稍后),回到自动修正之前的状态,然后学会单独留下该单词的特定实例。
我甚至不知道从哪里开始。我认为每个单词都包含在一个范围内是自满的,自动更正的单词包含一个特殊类和一个包含原始单词的data- *属性,监听自动更正单词的编辑,如果它被编辑回等于数据 - * value,添加一个类,使其脱离未来的自动更正轮次。
我在想,这可能是不必要的复杂,或者至少不是阻力最小的道路。这样做最聪明的方法是什么?
答案 0 :(得分:7)
您建议的方法(将span
中的每个单词分开并在其中存储其他数据)乍一看似乎是最明智的方法。在编辑器级别,您只需要确保所有文本都位于某个span
内,并且每个文本只包含一个单词(必要时将其拆分)。在单词级别上,只需监听span
s(绑定input
和propertyChange
)中的更改,并根据其类/数据进行操作。
然而,真正的痛苦是保持插入符号位置一致。当您更改textarea
或带有contentEditable
的元素的内容时,插入符号移动得相当不可预测,并且没有简单的(跨浏览器)方式来跟踪插入符号。我在SO和其他地方搜索了解决方案,我找到的最简单的工作解决方案是this blog post。不幸的是,它仅适用于textarea
,因此无法使用“跨度中的每个单词”解决方案。
所以,我建议采用以下方法:
Array
中保留一个单词列表,其中每个单词都存储当前值和原始值; textarea
的内容发生变化时,保留一组未更改的单词并重做其余单词; backspace
,则仅应用拼写检查; backspace
一次将撤消,并且不会再次检查除非修改。
backspace
将撤消一次更正,直到没有人留下。backspace
一次“保护”它。我在jsFiddle创建了一个简单的概念验证。详情如下。请注意,您可以将其与其他方法结合使用(例如,检测“向下箭头”键并显示带有一些自动更正选项的菜单)等。
proof-of-concept的步骤详细解释:
在Array
中保留一个单词列表,其中每个单词都存储当前值和原始值;
var words = [];
此正则表达式将文本拆分为单词(每个单词具有word
属性和sp
属性;后者存储紧跟其后的非单词字符)
delimiter:/^(\w+)(\W+)(.*)$/,
...
regexSplit:function(regex,text) {
var ret = [];
for ( var match = regex.exec(text) ; match ; match = regex.exec(text) ) {
ret.push({
word:match[1],
sp:match[2],
length:match[1].length + match[2].length
});
text = match[3];
}
if ( text )
ret.push({word:text, sp:'', length:text.length});
return ret;
}
当textarea
的内容发生变化时,请保留一组未更改的单词并重做其余单词;
// Split all the text
var split = $.autocorrect.regexSplit(options.delimiter, $this.val());
// Find unchanged words in the beginning of the field
var start = 0;
while ( start < words.length && start < split.length ) {
if ( !words[start].equals(split[start]) )
break;
start++;
}
// Find unchanged words in the end of the field
var end = 0;
while ( 0 < words.length - end && 0 < split.length - end ) {
if ( !words[words.length-end-1].equals(split[split.length-end-1]) ||
words.length-end-1 < start )
break;
end++;
}
// Autocorrects words in-between
var toSplice = [start, words.length-end - start];
for ( var i = start ; i < split.length-end ; i++ )
toSplice.push({
word:check(split[i], i),
sp:split[i].sp,
original:split[i].word,
equals:function(w) {
return this.word == w.word && this.sp == w.sp;
}
});
words.splice.apply(words, toSplice);
// Updates the text, preserving the caret position
updateText();
如果插入符号位于非单词字符之后(需要改进的空间)并且您没有点击backspace
,则仅应用拼写检查;
var caret = doGetCaretPosition(this);
var atFirstSpace = caret >= 2 &&
/\w\W/.test($this.val().substring(caret-2,caret));
function check(word, index) {
var w = (atFirstSpace && !backtracking ) ?
options.checker(word.word) :
word.word;
if ( w != word.word )
stack.push(index); // stack stores a list of auto-corrections
return w;
}
如果用户对更正不满意,点按backspace
一次将撤消,并且不会再次检查,除非修改。
$(this).keydown(function(e) {
if ( e.which == 8 ) {
if ( stack.length > 0 ) {
var last = stack.pop();
words[last].word = words[last].original;
updateText(last);
return false;
}
else
backtracking = true;
stack = [];
}
});
updateText
的代码只是将所有单词再次连接到一个字符串中,然后将值设置回textarea
。如果没有更改任何内容,或者在完成/撤消最后一次自动更正后放置插入符号,则考虑到文本长度的变化:
function updateText(undone) {
var caret = doGetCaretPosition(element);
var text = "";
for ( var i = 0 ; i < words.length ; i++ )
text += words[i].word + words[i].sp;
$this.val(text);
// If a word was autocorrected, put the caret right after it
if ( stack.length > 0 || undone !== undefined ) {
var last = undone !== undefined ? undone : stack[stack.length-1];
caret = 0;
for ( var i = 0 ; i < last ; i++ )
caret += words[i].word.length + words[i].sp.length;
caret += words[last].word.length + 1;
}
setCaretPosition(element,caret);
}
最终的插件结构:
$.fn.autocorrect = function(options) {
options = $.extend({
delimiter:/^(\w+)(\W+)(.*)$/,
checker:function(x) { return x; }
}, options);
return this.each(function() {
var element = this, $this = $(this);
var words = [];
var stack = [];
var backtracking = false;
function updateText(undone) { ... }
$this.bind("input propertyChange", function() {
stack = [];
// * Only apply the spell check if the caret...
// * When the contents of the `textarea` changes...
backtracking = false;
});
// * If the user was unsatisfied with the correction...
});
};
$.autocorrect = {
regexSplit:function(regex,text) { ... }
};
答案 1 :(得分:0)
假设您只提交插入符号左侧的单词,是否可以在键入空格字符或移动文本框插入符号之前禁用拼写检查程序?
我不确定这是否是你想要的那种答案。